Effective C++笔记: 构造/析构/赋值运算(二)

 

条款08:别让异常逃离析构函数

1.    最好不要在析构函数中抛出异常,如果一个析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不让异常跑出析构函数之外)或结束程序。

 

2.    如果客户需要对某个操作函数运行期间抛出的异常做出处理,那么class应该提供一个普通函数(而非析构函数)来执行该操作。

 

条款09:不要在构造和析构函数中调用virtual函数

1.    对构造函数,当base class的构造函数运行时,derived class的成员变量尚未初始化,因此virtual函数只会调用父类的版本;

 

在一个 derived class object(派生类对象)的 base class construction(基类构造)期间,object(对象)的类型是 base class(基类)的类型。不仅 virtual functions(虚拟函数)会解析到 base class(基类),而且用到 runtime type information(运行时类型信息)的语言构件(例如,dynamic_cast(参见 Item 27)和 typeid),也会将那个 object(对象)视为 base class type(基类类型)。

 

2.        同样的推理也适用于 destruction(析构)。一旦 derived class destructor(派生类析构函数)运行,这个 object(对象)的 derived class data members(派生类数据成员)就呈现为未定义的值,所以 C++ 就将它们视为不再存在。在进入 base class destructor(基类析构函数)时,这个 object(对象)就成为一个 base class object(基类对象),C++ 的所有构件—— virtual functions(虚拟函数),dynamic_casts ——都以此看待它。

 

有不同的方法来解决这个问题。其中之一是将 Transaction 中的 logTransaction 转变为一个 non-virtual function(非虚拟函数),这就需要 derived class constructors(派生类构造函数)将必要的日志信息传递给 Transaction constructor(构造函数)。那个函数就可以安全地调用 non-virtual(非虚拟)的 logTransaction。如下:

class Transaction {
public:
  explicit Transaction(
const std::string& logInfo);

  void logTransaction(const std::string& logInfo) const;   // now a non-virtual func
  ...
};

Transaction::Transaction(const std::string& logInfo)
{
  ...
  logTransaction(
logInfo);    // now a non- virtual call

class BuyTransaction: public Transaction {
public:
 BuyTransaction(
parameters )
 
: Transaction(createLogString( parameters ))  // pass log info to base class constructor

{ ... } 

private:
  static std::string createLogString( parameters );
};

换句话说,由于你不能在 base classes(基类)的 construction(构造)过程中使用 virtual functions(虚拟函数)向下匹配,你可以改为让 derived classes(派生类)将必要的构造信息上传给 base class constructors(基类构造函数)作为补偿。

在此例中,注意 BuyTransaction 中那个 (private) static 函数 createLogString 的使用。使用一个辅助函数创建一个值传递给 base class constructors(基类构造函数),通常比通过在 member initialization list(成员初始化列表)给 base class(基类)它所需要的东西更加便利(也更加具有可读性)。将那个函数做成 static,就不会有偶然触及到一个新生的 BuyTransaction object(对象)的 as-yet-uninitialized data members(仍未初始化的数据成员)的危险。因为static成员函数不包含this指针。

 

Item 10: assignment operators(赋值运算符)返回一个 reference to *this(引向 *this 的引用)

主要是为了链式等式的使用

 

Item 11: operator= 中处理 assignment to self(自赋值)

当一个 object(对象)被赋值给自己的时候,确保 operator= 是行为良好的。技巧包括比较 source(源)和 target objects(目标对象)的地址,关注语句顺序,和 copy-and-swap

 

Item 12: 拷贝一个对象的确保拷贝其所有组成部分

1.    拷贝函数应该保证拷贝一个对象的所有数据成员以及所有的base claass成分(容易疏忽)。简单的办法是在派生类的拷贝/赋值函数中调用基类的拷贝函数;

class PriorityCustomer: public Customer { // a derived class

. . .

PriorityCustomer::PriorityCustomer(const PriorityCustomer& rhs)
:   
Customer(rhs),                   // invoke base class copy ctor
  priority(rhs.priority)
{
  logCall("PriorityCustomer copy constructor");
}

PriorityCustomer&
PriorityCustomer::operator=(const PriorityCustomer& rhs)
{
  logCall("PriorityCustomer copy assignment operator");

  Customer::operator=(rhs);           // assign base class parts
  priority = rhs.priority;

  return *this;
}

 

2.    不要尝试以某个copying函数去实现另一个copying函数,应该将其共同的代码放进第三个函数中,并由2copying函数调用。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值