参考书籍: c++ primer 5
拷贝构造函数
- 定义
- 如果一个构造函数的第一个参数是自身类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。
- 何时发生拷贝初始化(即,调用拷贝构造函数)
- 用
=
定义变量时。
- 简记:=定义变量 。
- 将一个对象作为实参,传递给一个非引用类型的形参。
- 简记:拷贝传参。
- 从一个返回类型为非引用类型的函数返回一个对象。
- 简记:拷贝返回。
- 用花括号列表初始化一个数组中的元素或一个聚合类中的成员。
- 简记:花括号列表初始化。
使用的例子:
string a("hello world");
拷贝赋值运算符
先了解一些重载运算符:
重载运算符本质上是函数,其名字由operator
关键字后跟运算符组成。
如:operator=
。
- 重载赋值运算符的参数表示运算符的运算对象。
- 某些运算符,包括赋值运算符,必须定义为成员函数。
- 如果一个运算符是成员函数,其左侧对象就绑定到隐式的
this
参数。 - 对于二元的运算符,如赋值运算符,其右侧运算对象作为显式参数传递。
- 拷贝赋值运算符规范
- 为了与内置类型的赋值保持一致,赋值运算符通常返回一个指向其左侧运算对象的引用。
标准库通常要求保存在容器中的类型要具有赋值运算符,且其返回值是左侧对象的引用。
和拷贝构造函数类似,拷贝赋值运算符也是用于拷贝。
但是拷贝构造函数强调构造,拷贝复制运算符强调赋值。
为了确保你理解了这一点,给一个例子:
class A {
public:
A() = default;
A (int a) : a (a) { cout << "普通构造函数\n"; }
A (const A &right) : a (right.a) { cout << "拷贝构造函数\n"; }
int a;
};
int main (void)
{
A num (99); // 普通构造函数:直接初始化
A num1 = 99; // 普通构造函数:拷贝形式初始化
num = 99; // 普通构造函数:赋值,隐式调用了普通构造函数
A num2(num); //拷贝构造函数:直接初始化
A num3 = num; //拷贝构造函数:拷贝形式初始化
}
这个比较简单,输出即是注释标明的结果。
如果我们为A定义了拷贝赋值运算符,那么结果就会有变:
class A {
public:
A() = default;
A (int a) : a (a) { cout << "普通构造函数\n"; }
A (const A &right) : a (right.a) { cout << "拷贝构造函数\n"; }
A &operator= (int a) {cout << "拷贝赋值运算符\n"; return *this;}
int a;
};
int main (void)
{
A num (99); // 普通构造函数:直接初始化
A num1 = 99; // 普通构造函数:拷贝形式初始化
num = 99; // 拷贝赋值运算符:赋值
A num2(num); //拷贝构造函数:直接初始化
A num3 = num; //拷贝构造函数:拷贝形式初始化
}
关于num=99
为什么会走向不同的结果,这是因为没有对应拷贝赋值运算符的num=99
会自动调用普通构造函数,构造一个临时的A
类,然后再调用编译器合成的拷贝赋值运算符——(它接受一个A
的引用)。
class A {
public:
A() = default;
A (int a) : a (a) { cout << "普通构造函数\n"; }
A (const A &right) : a (right.a) { cout << "拷贝构造函数\n"; }
//A &operator= (int a) {cout << "拷贝赋值运算符\n"; return *this;}
A &operator=(const A&right){ cout << "拷贝赋值运算符2\n"; return *this; }
int a;
};
int main (void)
{
A num (99);
A num1 = 99;
num1 = 99;
}
这时你会看到有四条输出:
普通构造函数
普通构造函数
普通构造函数
拷贝赋值运算符2
而如果将A &operator=(const A&right)
定义为delete
,就无法编译通过。
此外,当定义了接受string
的拷贝赋值运算符时,这不符合我对预期:
class A {
public:
A() = default;
A (int a) : a (a) { cout << "普通构造函数\n"; }
A (const A &right) : a (right.a) { cout << "拷贝构造函数\n"; }
A &operator= (string a) { cout << "无用的\n"; return *this;};
//A &operator= (int a) {cout << "拷贝赋值运算符\n"; return *this;}
//A &operator=(const A&right){ cout << "拷贝赋值运算符2\n"; return *this; }
int a;
};
int main (void)
{
A num (99);
A num1 = 99;
num1 = 99;
A num2 (num);
A num3 = num;
}
因为合成的拷贝赋值运算符,应该只在一个类未定义拷贝赋值运算符时,这里依然正常工作了,也许是编译器更智能了,我使用-std=c++11
。