Effective c++ 学习笔记(item12)
这一章节依然介绍针对拷贝函数。很多时候我们需要定义自己的拷贝构造函数和拷贝赋值函数。自定义的时候要注意很多的细节。
1默认的拷贝构造函数和拷贝赋值函数什么时候不适用
- 情况1:当你需要在拷贝函数(拷贝构造函数和拷贝赋值函数的统称)中做一些个性化处理的时候,比如专门有些变量不用复制,比如赋值的时候做些log标记等等,这些事默认函数不会去做的。
- 情况2:类里面维护着资源,且存在指向资源的指针成员。这种情况如果调用默认的拷贝函数,就会出现多个对象的指针指向同一份资源,针对一个对象进行资源释放后,会导致其他对象的资源指针成为野指针。
2自定义的拷贝函数在维护上的困境
当你写好自己的拷贝函数后,后期在维护的时候,增加了类的新成员,此时开发人员不一定会记起来把这个新成员的拷贝操作也加入到拷贝函数中。而且这种情况编译器不会报警。因为编译器以为这是你的个性化操作。如果使用默认拷贝函数就不会有这个问题了,因为默认函数是在编译期间生成的,他能够保证所有的成员进行拷贝操作。所以可以用默认拷贝函数,最好用默认的。自定义拷贝函数的坑不止这个。
3继承类中自定义拷贝函数注意事项
默认的拷贝构造函数,能够保证构造出的对象和待拷贝对象完全一样,默认的拷贝构造函数会保证基类成分的拷贝构造,同时保证继承类成分的拷贝构造。如果是自己定义的拷贝构造函数,基类成分的拷贝构造就要自己显示的书写出来。
class PriorityCustomer: public Customer
{
PriorityCustomer(const PriorityCustomer& rhs);
PriorityCustomer& operator=(const PriorityCustomer& rhs);
private:
int priority;
}
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
: priority(rhs.priority)
{
log("prioritycustomer copy constructor");
}
PriorityCustomer& PriorityCustomer::Operator=(const PriorityCustomer& rhs)
{
log("PriorityCustomer copy assignment operator");
priority= rhs.priority;
return *this
}
上面的代码是有问题的,运行自定义的拷贝构造函数的时候,程序要先递归运行基类的构造函数,自定义的这个继承类构造函数并没有指定基类构造函数的入参,这种情况下,编译器会调用基类的默认构造函数(没有入参的构造函数)进行基类构造,然后构造继承类成分,然后初始化继承类成分中的priority变量为rhs.priority. 这就导致构造出来的类实例中基类成分跟我们期望要拷贝的不一致。
同样的在自定义的赋值函数中,这里并没有说明要给基类成分赋值,编译器以为这是你的个性化操作,连告警都不会提示你。所以正确的代码如下:
PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
: Customer(rhs), //告诉编译器基类构造的时候传入参rhs进去
priority(rhs.priority)
{
log("prioritycustomer copy constructor");
}
PriorityCustomer& PriorityCustomer::Operator=(const PriorityCustomer& rhs)
{
log("PriorityCustomer copy assignment operator");
Custom::operator=(rhs);//告诉编译器调用基类赋值函数赋值基类成分
priority= rhs.priority;
return *this
}
4减少自定义拷贝赋值函数和构造拷贝函数的代码冗余
这个就比较好理解了,做法也很通用,把共同的代码提炼出来作为一个private成员函数供两个拷贝函数分别调用即可。
网友评论