1 常函数
被const修饰过的成员函数,叫做常函数
格式:
int func(int num)const
① 已知通过对象调用成员函数时,编译器会自动把该对象的地址隐式传递给成员函数的this指针
② 如果对象被const修饰过,那么它的地址也带有const属性,就不能直接调用普通的成员函数了,编译器会报错,因此此时传递的对象地址是带有const属性,而普通成员函数的this指针默认不带const属性,C++编译器认为这个直接转换是非法的
③ 如果成员函数的this指针被const修饰后,就可以被const属性的对象调用,所以把普通成员函数定义为常函数,const本质上是修饰this指针
④ const属性的对象只能调用常函数,常函数也只能调用常函数
⑤ 构造函数不用,也大部分情况下不能设置为常函数
⑥ 同名的成员函数,如果其他的参数列表相同,但是常属性不同,也可以构成重载
⑦ 一般在常函数中,无法修改成员变量的值,但是在定义成员变量时,如果使用mutable来修饰,那么在常函数中也可以修改该成员变量的值
C语言和C++中的const有什么相同与不同?
相同:都是用来保护数据
不同:
C++编译器会优化const变量的取值过程,哪怕通过指针强制修改const变量的内存数据,但是编译器也不会去重新读取内存,这样的机制更为安全
C++中const修饰成员函数(修饰隐藏的this指针),定义常函数
一个空的结构体在C和C++中分别占多少字节?为什么?
C:0字节,C++:1字节。在C++中,这个结构可以定义成员函数,并且默认有四个成员函数(构造、析构、拷贝构造、赋值),在创建结构变量时也会自动调用构造
成员函数都自带一个隐藏的this指针参数,因此调用所有的成员函数都需要传递变量的地址
因此,这样的机制就要求结构变量必须在内存中至少占用1字节以上的内存,才能传递地址给成员函数,所以当结构体中没有成员时,编译器为了能传递结构地址,会让结构拥有1字节。
2 拷贝构造函数
拷贝构造是一种特殊的构造函数,无返回值,格式:类名(const 类名& that){}
何时会使用拷贝对象?
当时使用旧对象给新对象初始化时,会自动调用拷贝构造
Data d; // 无参构造
Data d1 = d; // 拷贝构造
Data* d2 = new Data(d); // 拷贝构造
void func(Data d){}
func(d); // 拷贝构造
拷贝构造任务:
顾名思义,拷贝构造负责把旧对象中的成员变量拷贝一份给新对象
隐藏的自动生成的拷贝构造函数会具有给每个成员变量拷贝的功能
拷贝构造函数什么情况需要显式实现
普通情况下,编译器自动生成的拷贝构造完全够用,不需要显式实现,但是当类中的成员有指针且为该该指针分配堆内存时,默认的拷贝构造函数只会对指针的值进行拷贝,这样就导致两个对象的指针成员都指向同一块堆内存,平时执行会脏数据,一旦执行析构函数时,就会导致重复释放堆内存,导致内存崩溃,这就叫做浅拷贝。此时,就需要显式实现拷贝构造(进行深拷贝)避免此类情况
浅拷贝与深拷贝
当类中有指针成员且为指针分配堆内存时,浅拷贝只会拷贝指针变量的值,深拷贝不拷贝指针变量的值,而是拷贝指针所指向内存的内容
3 赋值函数
一个旧对象给另一个旧对象赋值(两个对象都已经创建完成)时调用的成员函数
C++中把所有的运算符操作都当成函数处理,使用运算符时相当于在调用对应的运算符函数格式:类名的引用 operator=(const 类名& that){}
使用条件:
Test t1,t2;// 无参构造
t1=t2; // 赋值操作函数
赋值操作函数的任务
与拷贝构造基本一致,默认下编译器也会自动生成一个具有浅拷贝功能的赋值操作函数,一般情况够用,但是当需要深拷贝时,就需要显式实现拷贝构造函数以及赋值操作函数
实现赋值操作函数需要注意的问题
① 两个旧对象都有旧内存,因此需要先释放被赋值对象的旧内存,然后被赋值者根据拷贝内容再重新申请新内存,把赋值者内存中的内容拷贝给被赋值者的内存中
② 可能出现对象自己给自己赋值,通过判断this指针与赋值者的地址是否相同来决定是否执行赋值操作
③ 赋值运算符可以连续赋值,赋值操作函数的返回值类型为类类型的引用,才能连续赋值