如果一个类拥有非静态const成员或者引用成员,那么其不应该支持拷贝赋值操作。
首先,需要明确的是,如果一个类有非静态的const成员、引用成员,那么编译器合成的拷贝赋值函数是deleted
. 看以下这个例子:
class CT
{
public:
CT(int a) : x(a) {}
private:
const int x;
};
int main(int argc, char *argv[])
{
CT c(5);
CT cc = c;
c = cc;
return 0;
}
在main函数中首先做了直接初始化,然后是拷贝初始化——均没有问题。
但是到拷贝赋值时,编译报错:
error: use of deleted function 'CT& CT::operator=(const CT&)'
note: 'CT& CT::operator=(const CT&)' is implicitly deleted because the default definition would be ill-formed: class CT
error: non-static const member 'const int CT::x', can't use default assignment operator
即拷贝赋值被隐式删除了。这是因为: non-static const member ‘const int CT::x’, can’t use default assignment operator。
同理对有引用成员的类做拷贝赋值,会得到:
error: non-static reference member 'int& T::a', can't use default assignment operator
我们当然可以重写拷贝赋值函数,但是重写时要想保证编译正确且含义正确,其是不可能的!因为对const成员只能初始化,不能赋值;对引用变量赋值,改变的不是引用的指向,而是引用指向的变量的值。
所以,我们不应该让其支持拷贝赋值运算——要么我们不定义这样的类(通常来说意义不大——const是否必要?是否可以用static修饰?引用是否应该改为指针更加灵活?),要么我们显示将其定义为删除的。
最后,另外需要注意的相关点:
-
调用容器的push*操作一般会调用拷贝赋值(或移动赋值),这使得包含non-static const和引用的类成员不能直接放入到容器中。
-
拷贝赋值函数允许直接访问另一对象的private、protected成员。这正如初始化static成员一样,看起来似乎可以在类外访问private、protected成员。