复制构造函数 |
- 如果没有定义复制构造函数 – C++会自动提供
原型
class_name(const class_name&); Point(const Point&);
- 功能
- 逐个复制非静态成员的值 – 浅复制
- 如果含有成员的类型也是类, 则使用此成员的复制构造函数来复制此对象
- 当类成员里含有指针的时候,那么这两个对象的此成员都会指向同一个内存 – 这很不好
- 此时会使用new初始化指针成员,然后手动定义一个复制构造函数,进行深复制
- 注意要保证所有的构造函数里new的使用都与析构函数保持一致 – 有时候需要调整默认构造以实现这一点
- 此时会使用new初始化指针成员,然后手动定义一个复制构造函数,进行深复制
何时会调用
新建一个对象并将其初始化为同类现有对象时
Point a(b); Point a = b; Point a = Point(b); Point a = new Point(b);
每当程序生成对象副本的时候,都会调用复制构造函数
- 函数按值传递对象 或 函数返回对象时 – 可按引用传递
复制赋值函数 |
- 实质是赋值运算符的重载
- 如果没有定义, C++会自动提供
初始化类时不一定调用赋值运算符, 也可能只调用复制构造函数
初始化总会调用复制构造函数,但只是有可能调用赋值运算符
Point a = b; // 是否调用赋值运算符取决于实现
原型
class_name& class_name::operator=(const class_name&);
使用
- 同复制构造一样,当成员函数含有指针的时候,同样需要手动创建
- 手动创建时需要注意一些事项
- 需要先释放目标对象之前使用new分配的旧数据
- 应尽量避免将对象赋值给本身 – 否则可能会在赋值前释放其旧数据
- 应返回一个指向调用对象的引用 – 为了可以连续赋值
- 由此可知赋值函数并不创建新的对象
- 可以进一步重载赋值运算符,以支持其他类型到此类的转换
- 如果目前还没有必要实现复制构造和赋值运算符,但也不希望有复制或赋值现象发生,可将这两个函数声明为private且忽略其实现(将永远不会被调用)
重载[]运算符 |
对于
[]
操作符,两个操作数一个在[
左边,一个在[]
里面operator[](type_name);
a[4]
等价于a.operator[](4);
- 有时候将返回类型声明为引用,即可使用
[]
对元素(即使是私有成员)进行赋值 当对象为const类型时,便不能使用
[]
进行赋值此时需要声明一个const版本的
[]
重载版本const return_name& oeprator[](typename) const;
类中的new和delete* |
在类中使用new的注意事项
- 构造函数中使用new, 析构函数中要使用delete
- new对应delete – new[] 对应delete[]
- 如果有多个构造函数,需要以相同的方式使用new – 只有一个析构函数
- 也可以将指针初始化为空 –
0 NULL nullptr(C++11)
– delete可以用于空指针
- 也可以将指针初始化为空 –
应当定义一个复制构造函数,通过深度复制将一个对象初始化为另一个对象
String::String(const String& s) { len = s.len; str = new char[len+1]; // 分配空间 -- 深复制 strcpy(str, s.str); }
应当定义一个赋值运算符,通过深度复制将一个对象复制给另一个对象
String& String::operator=(const String& s) { // 返回引用 -- 防止创建额外副本 if(this == &s) return *this; // 自我赋值时 -- 直接返回this delete[] str; // 清理旧数据 len = s.len; str = new char[len+1]; strcpy(str, s.str); return *this; }
函数的返回对象类型 |
返回指向const对象的引用
- 返回对象将调用复制构造函数,而返回引用不会
- 返回引用将提高效率
- 返回的引用不能是所在函数的局部变量
- const类型引用的返回值也应该是const
返回const类型引用可使函数在调用的时候不被修改
const int& Max(int& x, int& y); Max(a, b)++; // 这样便是不对的, 但如果不是const的话便可以
返回const引用的函数可以赋值给此类型变量(赋值了一下),也可赋值给const类型的引用,但是不能赋值给非const的引用
int c = Max(a, b); // 正确 const int& c = Max(a, b); // 正确 int& c = Max(a, b); // 错误
const Point& Max(const Point& a, const Point& b) { if(a.x > b.x) return a; return b; }
返回指向非const对象的引用
- 有时候是为了提高效率 – 如重载赋值运算符
- 也可以返回对象,也可以返回引用 – 返回引用可避免创建新的副本
- 有时候是为了必须这样做 – 如重载
cout的<<
运算符
- ostream对象没有公有的复制构造函数
- 有时候是为了提高效率 – 如重载赋值运算符
- 返回对象
- 返回对象是函数中的局部变量时,只能使用返回对象而非引用 – 如重载+运算符
- 存在复制构造函数创建返回的对象的开销,但无法避免
- 返回const对象
- 主要是为了防止函数(或重载了操作符的表达式)本身成为左值而导致的一些问题
- 如
a+b = x;
这种式子无意思 且如果用在if()时还有可能出现不易察觉的错误
- 如
- 主要是为了防止函数(或重载了操作符的表达式)本身成为左值而导致的一些问题
指向对象的指针 |
Point* p = new Point(1, 2);
cout << p->get_x() << endl;
delete p;
定位new运算符不能用delete释放 – 可显示调用析构函数
char* buffer = new buffer[maxn]; Point* p1 = new(buffer) Point; Point* p2 = new(buffer + sizeof(Point)) Point(1, 2); p2->~Point(); p3->~Point(); delete[] buffer;