Effective C++
  • 介绍
  • 0.导读
  • 1.让自己习惯 C++
    • 条款01:视 C++ 为一个语言联邦
    • 条款02:尽量以 const, enum, inline 替换 #define
    • 条款03:尽可能使用 const
    • 条款04:确定对象被使用前已先被初始化
  • 2.构造/析构/赋值运算
    • 条款05:了解 C++ 默默编写并调用哪些函数
    • 条款06:若不想使用编译器自动生成的函数,就该明确拒绝
    • 条款07:为多态基类声明 virtual 析构函数
    • 条款08:别让异常逃离析构函数
    • 条款09:绝不在构造和析构过程中调用 virtual 函数
    • 条款10:令 operator= 返回一个reference to *this
    • 条款11:在 operator= 中处理自我赋值
    • 条款12:复制对象时勿忘其每一个成分
  • 3.资源管理
    • 条款13:以对象管理资源
    • 条款14:在资源管理类中小心 copying 行为
    • 条款15:在资源管理类中提供对原始资源的访问
    • 条款16:成对使用 new 和 delete 时要采用相同形式
    • 条款17:以独立语句将 newed 对象置入智能指针
  • 4.设计与声明
    • 条款18:让接口容易被正确使用,不易被误用
    • 条款19:设计 class 犹如设计 type
    • 条款20:宁以 pass-by-reference-to-const 替换 pass-by-value
    • 条款21:必须返回对象时,别妄想返回其 reference
    • 条款22:将成员变量声明为 private
    • 条款23:宁以 non-member、non-friend 替换 member 函数
    • 条款24:若所有参数皆需类型转换,请为此采用 non-member 函数
    • 条款25:考虑写出一个不抛异常的 swap 函数
  • 5.实现
    • 条款26:尽可能延后变量定义式的出现时间
    • 条款27:尽量少做转型
    • 条款28:避免返回 handles 指向对象内部成分
    • 条款29:为”异常安全“而努力是值得的
    • 条款30:透彻了解 inlining 的里里外外
    • 条款31:将文件间的编译依存关系降至最低
  • 6.继承与面对对象设计
    • 条款32:确定你的 public 继承塑模出 is-a 关系
    • 条款33:避免遮掩继承而来的名称
    • 条款34:区分接口继承和实现继承
    • 条款35:考虑 virtual 函数以外的其他选择
    • 条款36:绝不重新定义继承而来的 non-virtual 函数
    • 条款37:绝不重新定义继承而来的缺省参数值
    • 条款38:通过复合塑模出 has-a 或”根据某物实现出“
    • 条款39:明智而审慎地使用 private 继承
    • 条款40:明智而审慎地使用多重继承
  • 7.模板与泛型编程
    • 条款41:了解隐式接口和编译期多态
    • 条款42:了解 typename 的双重含义
    • 条款43:学习处理模板化基类内的名称
    • 条款44:将与参数无关的代码抽离 templates
    • 条款45:运用成员函数模板接收所有兼容类型
    • 条款46:需要类型转换时请为模板定义非成员函数
    • 条款47:请使用 traits classes 表现类型信息
    • 条款48:认识 template 元编程
  • 8.定制 new 和 delete
    • 条款49:了解 new-handler 的行为
    • 条款50:了解 new 和 delete 的合理替换时机
    • 条款51:编写 new 和 delete 时需固守常规
    • 条款52:写了 placement new 也要写 placement delete
  • 9.杂项讨论
    • 条款53:不要请忽编译器的警告
    • 条款54:让自己熟悉包括 TR1 在内的标准程序库
    • 条款55:让自己熟悉 Boost
Powered by GitBook
On this page

Was this helpful?

  1. 2.构造/析构/赋值运算

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

Never call virtual functions during construction or destruction.

本条款重点:不该在构造函数和析构函数期间调用 virtual 函数。

derived class 对象内的 base class 成分会在 derived class 自身成分被构造之前先构造妥当,此时如果 base class 的构造函数中调用了 virtual 函数,该 virtual 函数是 base class 版本的,而非 derived class 的。这种现象的根本原因在于,在 derived class 对象中的 base class 构造期间,对象类型是 base class 而不是 derived class。不过好在某些编译器会发出警告。

潜在的调用 virtual 函数问题

如果 base class 有多个构造函数,每个都需要执行某些相同的工作,那么为了避免重复的代码,需要将共同初始化代码放进一个初始化函数中,如果此时在初始化函数中调用了 virtual 函数,编译器和连接器是不会有提示的。

class Transaction {
public:
    Transaction() { init(); }                    // 调用 non-virtual;
    virtual void logTransaction() const = 0;
private:
    void init () {
        ...
        logTransaction();                        // 调用 virtual;
    }
};

解决办法

首先,可以让构造函数和析构函数都不去调用 virtual 函数,以避免这种问题。

其次,如果无法避免,可以将函数改为 non-virtual,然后要求 derived class 构造函数传递必要信息给 base class 的构造函数。

class Transacion {
public:
    explicit Transaction(const std::string& logInfo);
    void logTransaction(const std::string& logInfo) const;
};

Transaction::Transaction(const std::string& logInof) {
    ...
    logTransaction(logInfo);    // 调用 non-virtual 版本;
}

class BuyTransaction: public Transaction {
public:
    BuyTransaction(parameters): Transaction(createLogString(parameters)) {}
private:
    static std::string createLogString(parameters);
};

这里使用 private static 函数的作用在于避免了意外指向“初期未成熟的 derived class 对象内部尚未初始化的成员变量”。

Previous条款08:别让异常逃离析构函数Next条款10:令 operator= 返回一个reference to *this

Last updated 6 years ago

Was this helpful?