Effective C++: Item 39 -- Use private inheritance judiciously

Effective C++ 同时被 2 个专栏收录
11 篇文章 0 订阅

注: 此为英文资料整理,如需翻译请私信或留评论

Definition

Private inheritance doesn’t mean is-a. In contrast to public inheritance, compilers will generally not convert a derived class object into a base class object if the inheritance relationship between the classes is private.

Example

class Person { ... };
class Student: private Person { ... };
void eat(const Person& p);
void study(const Student& s);
Person p; Student s;
eat(p); // fine p is a person
eat(s); // not ok, s is not a person

So how does private inheritance behave?

Members inherited from a private base class become private members of the derived class, even if they were protected or public in the base class. Thus, private inheritance means is-implemented-in-terms-of. It is used purely as an implementation technique. Private inheritance means nothing during software design, only during software implementation.


How do we choose between private inheritance and composition?

Use composition whenever you can, only use private inheritance when necessary.

When is necessary?

1. When you’re dealing with two classes not related by is-a where one either needs access to the protected members of another or needs to redefine one or more of its virtual functions.

Example:

Suppose we’re working on an application involving Widgets, and we decide we need to better understand how Widgets are being used. We decide to modify the Widget class to keep track of how many times each member function is called by reusing the existing Timer class.

class Timer { 
public:
	explicit Timer(int tickFrequency); 
	virtual void onTick() const; // automatically called for each tick
	... 
};

We cannot use public inheritance since Widgets is not a Timer. Thus, we use private inheritance.

class Widget: private Timer { 
private:
	virtual void onTick() const;
	... 
};

By virtue of private inheritance, Timer’s public onTick function becomes private in Widget, and we keep it there when we redeclare it.

Yet another way of solving this question using composition

We’d just declare a private nested class inside Widget that would publicly inherit from Timer, redefine onTick there, and put an object of that type inside Widget.

class Widget { 
private:
	class WidgetTimer: public Timer {
	public:
		virtual void onTick() const;
		... 
	};
	WidgetTimer timer;
	... 
};

Two reason to use this method over the private inheritance:

  1. Design Widget to allow for derived classes, but prevent derived classes from redefining onTick.
    Derived classes may redefine virtual functions even if they are not permitted to call them.
  2. Minimize Widget’s compilation dependencies.
    If Widget inherits from Timer, Timer’s definition must be available when Widget is compiled, so the file defining Widget probably has to #include Timer.h.
    On the other hand, if WidgetTimer is moved out of Widget and Widget contains only a pointer to a WidgetTimer, Widget can get by with a simple declaration for the WidgetTimer class; it need not #include anything to do with Timer

2. When space concerns can tip the scales toward private inheritance.

EBO – Empty Base Optimization

Empty class: Such classes have no non-static data members; no virtual functions (because the existence of such func- tions adds a vptr to each object); and no virtual base classes (because such base classes also incur a size overhead)

class Empty {}; // has no data, so objects should // use no memory
class HoldsAnInt {  
private:
	int x; 
	Empty e; // should need only space for an int // should require no memory
};

However, sizeof(HoldsAnInt) > sizeof(int) because C++’s edict against zero-size freestanding objects is typically satisfied by the silent insertion of a char into “empty” objects.
This constraint doesn’t apply to base class parts of derived class objects, because they’re not freestanding.

class HoldsAnInt: private Empty { 
private:
	int x; 
};

You’re almost sure to find that sizeof(HoldsAnInt) == sizeof(int). This is known as the empty base optimization (EBO)


Things to Remember

  • Private inheritance means is-implemented-in-terms of. It’s usually inferior to composition, but it makes sense when a derived class needs access to protected base class members or needs to redefine inherited virtual functions.
  • Unlike composition, private inheritance can enable the empty base optimization. This can be important for library developers who strive to minimize object sizes.
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

GetRekt

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值