Effective C++读书笔记(四)构造,析构,赋值运算部分(下)

读书笔记中涉及到的所有代码实例可通过https://github.com/LuanZheng/EffectiveCPlusPlus.git进行下载获得。

Item9 绝不在构造和析构函数中调用virtual函数

Base class构造函数的执行更早于derived class构造函数,因此,base class构造期间virtual函数绝不会下降到derived class阶层。或者说,在base class构造期间,virtual函数不是virtual函数。
相同道理也适用于析构函数。一旦derived class析构函数开始执行,对象内的derived class成员变量便呈现未定义值,所以C++视它们仿佛不再存在。

若在构造函数或析构函数中需要使用类似virtual带来的多态的便利性的话,一种做法是在base class将原来的虚函数(构造函数将调用的那个)改为非虚函数,然后要求派生类构造函数传递必要信息给基类构造函数,而后那个构造函数便可安全的调用此非虚函数。

小插曲:建立工程文件时,开始命名为Item8,之后修改为Item9之后,需要手动在Item9.vcxproj里面设置一下。然后在IDE里面移除工程,再重新加载。

例子见Item9

Item10 令operator=返回一个reference to *this
为了实现“连锁赋值”(x=y=z=15),赋值操作符必须返回一个reference指向操作符左侧实参。

#ifndef _COMPLEX_H_
#define _COMPLEX_H_
class Complex
{
public:
       Complex();
       Complex(const double real, const double image);
       Complex& operator=(const Complex& rhs);
       Complex& operator+=(const Complex& rhs);
       void print();
private:
       double m_RealPart;
       double m_ImagePart;
};
#endif // !_COMPLEX_H_

#include "Complex.h"
int main()
{
       Complex c1(1.7, 1.6);
       c1.print();
       Complex c2(0.9, 1.2);
       c2.print();
       Complex c3;
       c3.print();
       c3 += c2 += c1;
       c1.print();
       c2.print();
       c3.print();
       c1 = c2 = c3;
       c1.print();
       c2.print();
       c3.print();
       return 0;
}

例子见Item10

条款11 在operator=中处理“自我赋值”

在operator=中出现自我赋值时,需要进行先保存旧内存,分配新内存,copy,然后删除旧内存的顺序。因为如果先删除内存,一旦=两边指的是同一地址,则在删除内存时,就全部删除了。后面再赋值时,传入的数据已经变成了空指针。

#ifndef _WIDGET_H_
#define _WIDGET_H_
#include "Bitmap.h"
class Widget
{
public:
       Widget();
       ~Widget();
       Widget& operator=(const Widget& rhs);
private:
       Bitmap *pb;
};
#endif // !_WIDGET_H_

#include "Widget.h"
Widget::Widget()
{
       pb = new Bitmap();
}
Widget::~Widget()
{
       if (0 != pb)
       {
              delete pb;
              pb = 0;
       }
}
Widget& Widget::operator=(const Widget& rhs)
{
       //if (0 != pb)
       //{
       //     delete pb; //这段代码存在问题,若rhs和this指的是同一个对象,则在delete pb时,同时也delete了rhs
       //     pb = 0;   //后面copy构造函数运行就会报错。因为rhs.pb为空指针
       //}
       Bitmap* pOrig = pb;  //修正后的代码,先保存一下this->pb,然后分配新内存,最后删除掉原内存
       pb = new Bitmap(*rhs.pb);  //运算符优先级,先运算.然后是*,相当于(*(rhs.pb))
                            //相当于利用Bitmap的copy构造函数,利用rhs.pb副本,使得this->pb指向这个副本
       if (0 != pOrig)           
       {
              delete pOrig;
              pOrig = 0;
       }
       return *this;
}
#include "Widget.h"
int main()
{
       Widget w1;
       w1 = w1;       //实际上在内存中做了一次交换,新分配内存空间存放W1,之后删除原来的W1
       return 0;
}

例子见Item11

Item12: 复制对象时勿忘其每一个成分

如果你为class添加一个成员变量,你必须同时修改copying函数。(你也需要修改class的所有构造函数以及任何非标准形式的operator=)

任何时候只要你承担起“为派生类撰写拷贝构造函数”的重责大任,必须很小心的也复制其基类成分。那些成分往往是private,所以你无法直接访问它们,你应该让派生类的拷贝构造函数调用相应的基类函数。

注意:虽然派生类中无法访问基类的private成员,但基类成员变量的内存还是被派生类持有的,也就是说,当为基类传递一个派生类对象时,在基类中,就可以使用这个成员变量。

如果你发现你的拷贝构造函数和拷贝复制操作符有相近的代码,消除重复代码的做法是,建立一个新的成员函数给两者调用。这样的函数通常为private的,且通常被命名为init.

#include "Derived.h"
Derived::Derived()
{
}
void Derived::setY(int y)
{
       m_Y = y;
}
int Derived::getY()
{
       return m_Y;
}
Derived::Derived(const Derived& derived) : Base(derived), m_Y(derived.m_Y)
{
}

例子见Item11

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值