参考书籍: 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
。