C++语言学习03

发布时间 2023-08-29 20:56:07作者: 优秀还天籁

一、对象的创建和销毁过程分析
1、对象的创建过程
a、给对象划分内存空间
b、执行初始化列表
1、根据继承表(类名的后面)的顺序调用父类的无参构造或者有参构造
通过:父类名(val) 调用父类的有参构造
2、根据成员变量的定义顺序调用类类型成员的无参构造或者
有参构造
通过:类类型成员名(val) 调用类类型成员的有参构造
3、对其他成员初始化
c、执行自己的构造函数、可能去申请资源

2、对象的销毁过程(创建的逆序)
    a、执行自己的析构函数、可能去释放资源
    b、根据类类型成员顺序的逆序,调用它们的析构函数
    c、根据继承表的逆序,调用父类的析构函数
    d、释放对象的内存

二、成员函数是如何区分调用它的对象-使用隐藏的this指针

1、对象的内存只存储了成员变量,没有存储成员函数的指针,相当于所有对象调用的是同一份成员函数
2、当对象调用成员函数时,编译器会自动把对象的地址传递给该成员函数,也就是说普通的成员函数中都有一个
隐藏的参数,该参数名字叫做this指针,this指针用来接收调用对象的地址
3、this指针拿到了调用对象的地址后,就可以直接访问该对象的成员,完成区分对象的任务
4、虽然this指针时隐藏定义的,但是可以显式的使用它,但不要多此一举的显示定义它

三、常函数
1、被const修饰了this指针的成员函数,被称为常函数
2、当对象调用成员函数时,编译器会隐式的把对象地址传递给成员函数
3、当对象被const修饰过具有常属性,就不能直接调用普通成员函数,
因为传递的对象地址也具有常属性,而普通成员函数的this指针参数不具备
常属性,所以编译器会报错,C++编译器不允许用带常属性的指针数据给不带
常属性的指针变量赋值
4、因此需要让成员函数中的this也具备常属性,通过const修饰变成常函数
所以const其实修饰的事this指针,这样就可以让具有常属性的对象调用常
函数

    返回值 类名::成员函数(参数列表)const
    {
        //常函数
    }
5、具有常属性的对象只能调用常函数,常函数中也只能调用常函数吗;不具有常属性的
对象都可以调用
6、同名的成员函数,如果其他参数列表完全相同,但是常属性不同,也可以构成重载
7、正常来说在常函数中是不能修改成员变量的,除非该成员在定义时通过 mutable
修饰

1、const在c和c++中的相同点和不同点
相同点:const在c和c++中都是用来"显式"的保护数据不被修改

不同点:
    a、C++编译器会优化const变量的取值过程,哪怕该变量的内存
    被强行修改也不会改变通过变量访问的数值,这种机制会更安全,
    但是C语言编译器不会优化
    b、在C++中const还可以修饰成员函数的this指针,从而定义常函数

2、一个空结构体在c语言、C++中分别占多少字节,为什么
    在C语言中空结构体占0字节
    在C++中空结构体占1字节

    原因:在C++结构体中可以定义成员函数,并且默认有四个隐藏的成员
    函数(构造、析构、拷贝构造、赋值),当对象去调用成员函数时,需要
    传递对象的地址给成员函数,这种机制就要求结构对象需要在内存中有一席
    之地,所以如果结构没有任何成员变量,编译器会让结构至少拥有1字节的
    不使用的内存,让上面这套机制自恰。

四、拷贝构造
拷贝构造就是一种特殊版本的构造函数,格式为
类名(const 类名& that )
{
// 执行给每个新对象
}

什么时候会调用拷贝构造:
    当使用旧对象给新对象初始化时,会自动调用拷贝构造
    Test t1;        //  调用无参构造
    Test t2 = t1;   //  调用拷贝构造
    Test t3(t2);    //  调用拷贝构造

拷贝构造的任务:
    负责把旧对象中的成员变量拷贝给新对象,并且编译器默认
    已经自动生成具有该功能的拷贝构造函数

什么时候需要显式的写出拷贝构造
    普通情况下编译器自动生成的拷贝构造完全够用,但当类中有成员是指针
    类型且为该指针成员分配了堆内存,使用默认自动生成的拷贝构造只会对
    指针的值进行拷贝,此时就会导致两个对象的指针成员指向同一块内存,
    所以在执行析构函数时会造成重复释放错误,此时应该显式的实现拷贝构造

浅拷贝和深拷贝:
    浅拷贝:当类中的成员有指针且分配堆内存,只拷贝指针变量的值
    深拷贝:不拷贝指针变量的值,而是拷贝指针变量所指向的内容

五、赋值操作(拷贝赋值、赋值运算符函数)
任务:就是用一个旧对象给另一个旧对象赋值(两个对象都已经完成创建)
注意:在C++中会把运算符当作函数处理,使用运算符号时会调用运算符函数
Test t1,t2; //无参构造
t1 = t2; o //赋值操作函数

类名& operator=(const 类名& that)
{

}

什么时候需要显式的写赋值操作?
    普通情况一般不需要显式的写赋值操作
    类似于需要显式的写拷贝构造一样,当需要进行深拷贝时,就需要显式的写拷贝构造
    和赋值操作

实现赋值操作需要注意的问题:
    虽然赋值操作与拷贝操作的任务相同,都需要深拷贝,但是环境不同(旧对象、新对象)
    问题1:被赋值的对象的指针已经分配有内存
        a、先释放被赋值者的指针指向的原内存
        b、根据赋值者的赋值重新申请新内存
        c、把赋值者内存的内容深拷贝到新内存中

    问题2:可能出现对象自己给自己赋值的情况
        通过this指针与赋值者的地址是否相同,如果相同立即返回*this结束,如果不相同
        才进行赋值操作

补充:
string类的变量转变成char*
string path
path.c_str();