类与对象
拷贝构造函数
-
类名 (const 类名 &rhs)
-
默认情况下编译器会自动生成拷贝构造函数
(浅拷贝,如果有用到new需要手写一个深拷贝)
//常见调用形式
Point pt1(1, 2);
Point pt2 = pt1;
调用时机
-
用一个已经存在的对象初始化另一个新对象
-
函数的形参和传入的实参都是对象,进行实参与形参的结合时
-
当函数的返回值是对象,函数调用完成返回时
(编译器优化选项 -fno-elide-constructors 关闭优化
C++标准允许一种(编译器)实现省略创建一个只是为了初始化另一个同类型对象的临时对象,实际上调用了拷贝构造函数之后析构)
注意const和&
-
使用&原因:不可以去掉&,值传递要创建对象,创建对象要初始化调用拷贝构造函数,如此循环使用拷贝构造函数直到栈溢出,防止拷贝构造函数进入死循环。
-
使用const原因:不可以去掉const,非const左值引用不能绑定到右值。当传递右值的时候,在执行拷贝构造函数的时候无法匹配右值。
浅拷贝
-
-
-
两者指向同一个堆空间的字符串,这种只拷贝指针地址的方式,我们称为浅拷贝。当其中一个对象被销毁时,另外一个对象就获取不到相应的_brand 值了。
深拷贝
-
-
-
因为两个对象都拥有各自的独立堆空间字符串,一个对象销毁时就不会影响另一个对象。
lvalue与rvalue
-
左值是对应内存中有确定存储地址的对象的表达式的值,其余为右值
-
字面值常量(1234567890)、匿名对象、临时对象都属于右值的一种形式,比如函数
-
左值可以放到赋值符号左边,非const的左值不能=(绑定)到右值
-
右值可能存在寄存器上或者栈上超短时间就消失
-
左值:可以取地址
-
右值:不可取地址
隐含this指针
-
*const this指向对象本身,隐藏于(非静态)成员函数第一个参数的位置
-
可以修改指针内容,不能修改指向
-
类的所有对象共用这个成员函数体。 当程序被编译之后,此成员函数地址即已确定。而成员函数之所以能把属于此类的各个对象的数据区别开, 就是靠这个this指针。函数体内所有对类数据成员的访问, 都会被转化为this->数据成员的方式。
-
.左边是实体,提取右边成员
-
->左边是指针,也可以提取指向的实体的成员
赋值运算符函数
- 类通过调用成员函数操作数据成员
- 在类外定义时作用域限定符加在&右边
//常见调用形式
pt2.operator=(pt1);
pt2 = pt1;//浅拷贝
默认浅拷贝
- 只拷贝指针地址
- 赋值运算符函数
//默认情况下,编译器会自动生成赋值运算符函数
Point &operator=(const Point &rhs){
_ix=rhs._ix;
_iy=rhs._iy;
return *this;
}
正确深拷贝
-
拷贝指针所指内容
-
构造函数调用之后就有一块空间,正确做法
-
先delete再nullptr
-
之后申请新的空间让其指向新空间,把内容拷过来,使用深拷贝
(直接拷过来可能会内存不够)
-
-
赋值运算符函数
Compter & operator=(const Computer &rhs){
//防止不能自赋值
if(this != &rhs){//不能使用*this!= rhs尚未定义运算!=表示所指内容不等
//释放原始堆空间
delete [] _brand;
_brand = nullptr;
//深拷贝
_brand = new char[strlen(rhs._brand)+1]();
strcpy(_brand,rhs._brand);
_price = rhs._price;//常量浅拷贝没关系
}
return *this;//返回this解引用内容
}
注意返回类和&
-
赋值运算符返回&符号:不可以去掉,会在return语句中重复执行拷贝构造函数
-
赋值运算符函数返回类型是类:不可以修改,连续赋值时会报错
-
参数列表中的const和&思考方式同拷贝构造函数
特殊数据成员
常量const数据成员
- 初始化必须在初始化列表中,不能在构造函数等任何成员函数内赋值
引用&数据成员
-
初始化必须在初始化列表中
-
引用占用一个指针大小的空间
-
只有数据成员算占类的大小,其余编译成二进制文件存放在程序代码区(只读段),sizeof(类名),64位机器占8B
-
sizeof(对象) > 0 是因为标准规定完整对象的大小为正数,若为0,则不能区分该类实例化出的实例,为了实现每个实例在内存中都有一个独一无二的地址,编译器往往会给一个空类隐含的加一个字节
类对象成员
-
初始化必须在初始化列表中,对所有的参数初始化
(比如一个直线类Line 对象中包含两个Point 类对象,对
Point对象的创建就必须要放在Line 的构造函数的初始化列表中进行)
静态static数据成员
-
静态数据成员初始化必须在类的外面,不能在初始化列表中初始化
-
如果放在头文件中会有在执行文件和类定义文件中多重定义,应放在实现文件中
(#ifndef 只能防止在一个文件中重定义)
-
静态数据成员不受pubic、private修饰符限制
(在全局静态区中,不占用类的大小,float因为内存对齐与指针一起成为成员函数时会变成8B)
特殊成员函数
静态static成员函数
-
静态成员函数在类外static应省略简写,不然会报错
-
静态成员函数的参数列表中不含有隐含的this指针,被所有对象共享
-
静态成员函数内部只能调用静态数据成员和静态的成员函数
(如果要调用非静态数据成员可以在参数列表使用&传参)
-
直接通过类名和作用域限定符也可以调用静态成员函数,普通成员函数不能这样调用(未实例化)
常量const成员函数
-
放在函数小括号后面、花括号前面修饰
-
两个同名的成员函数其中一个为const就可以重载
(因为其中一个参数列表隐含*const this,
而const成员函数隐含const *const this)
-
只能读取类的数据成员不能修改
(只读操作建议先定义const版本的)
-
只能调用const成员函数不能调用非const成员函数
放在函数小括号后面、花括号前面修饰 -
两个同名的成员函数其中一个为const就可以重载
(因为其中一个参数列表隐含*const this,
而const成员函数隐含const *const this)
-
只能读取类的数据成员不能修改
(只读操作建议先定义const版本的)
-
只能调用const成员函数不能调用非const成员函数