您现在的位置是:首页 >技术杂谈 >Effective C++读书笔记——item39(谨慎使用私有继承)网站首页技术杂谈

Effective C++读书笔记——item39(谨慎使用私有继承)

使大国和共民人华中 2026-03-17 00:01:05
简介Effective C++读书笔记——item39(谨慎使用私有继承)
  1. 私有继承的行为与公有继承的对比
    • 公有继承:表示 “is - a” 关系,如class Student : public Person,在必要时Student可隐式转型为Person
    • 私有继承:编译器通常不会将派生类对象(如Student)转型为基类对象(如Person),且从私有基类继承的成员会成为派生类的私有成员,即使它们在基类中是保护或公有的。例如:
class Person { /*... */ };
class Student : private Person { /*... */ };
void eat(const Person& p);
void study(const Student& s);
Person p;
Student s;
eat(p); // 正常,p是Person
eat(s); // 错误,Student不是Person
  1. 私有继承的含义:私有继承意味着 “is - implemented - in - terms - of”(是根据…… 实现的),纯粹是一种实现技术,仅在软件实现期间有意义,在软件设计期间无意义。例如:
class Timer {
public:
    explicit Timer(int tickFrequency);
    virtual void onTick() const;
    /*... */
};
class Widget : private Timer {
private:
    virtual void onTick() const;
    /*... */
};

这里Widget私有继承Timer,是因为Widget需要利用Timer的特性,如onTick函数,而不是因为WidgetTimer有概念上的 “is - a” 关系。 3. 私有继承与复合的选择

  • 尽可能使用复合,只有在绝对必要时才用私有继承。绝对必要的情况主要是当涉及保护成员和 / 或虚函数时,以及一种与空间相关的极端情况。
  • 当用复合代替私有继承时,可在派生类内声明一个从基类公有继承的私有嵌套类,并在派生类中放置该类型的对象。例如:
class Widget {
private:
    class WidgetTimer : public Timer {
    public:
        virtual void onTick() const;
        /*... */
    };
    WidgetTimer timer;
    /*... */
};

这种设计比单纯使用私有继承更复杂,包含了公有继承和复合,还引入了新类WidgetTimer。但它有两个优点:一是可禁止派生类重定义onTick函数;二是可最小化Widget的编译依赖。 4. 私有继承的特殊情况 - 空基优化(EBO)

  • 当处理没有非静态数据成员、虚函数和虚拟基类的空类时,若使用复合,空类数据成员会占用空间;若使用私有继承,可实现空基优化,使派生类对象大小不增加空类的额外空间。例如:
class Empty {};
class HoldsAnInt {
private:
    int x;
    Empty e;
};
// sizeof(HoldsAnInt) > sizeof(int)

class HoldsAnInt : private Empty {
private:
    int x;
};
// sizeof(HoldsAnInt) == sizeof(int)

但 EBO 通常只在单继承下可行,且 “空” 类实际常包含 typedefs、enums、静态数据成员或非虚函数。

总之,私有继承意味着 “is - implemented - in - terms - of”,通常比复合低级。使用私有继承时应谨慎,需考虑所有可选方案,只有当它是表示类之间关系的最佳方法时才使用,比如在需要访问保护成员、重定义虚函数或实现空基优化等特定情况下。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。