在The C++ Programming Language , 4th Edition 的第 20.5.2 节“访问基类”(第 605 页)中,它说(关于私有继承):
当通过将接口限制为基类来定义类时,私有基类最有用,这样可以提供更强的保证。例如,B 是 Z 的实现细节。指针模板的向量从§ 25.3 就是一个很好的例子。
目前尚不清楚 Bjarne Stroustrup 在这里想说什么。如何通过将“接口”限制为基础来定义类?他所说的“更有力的保证”是什么意思?
在The C++ Programming Language , 4th Edition 的第 20.5.2 节“访问基类”(第 605 页)中,它说(关于私有继承):
当通过将接口限制为基类来定义类时,私有基类最有用,这样可以提供更强的保证。例如,B 是 Z 的实现细节。指针模板的向量从§ 25.3 就是一个很好的例子。
目前尚不清楚 Bjarne Stroustrup 在这里想说什么。如何通过将“接口”限制为基础来定义类?他所说的“更有力的保证”是什么意思?
让我们举一个非常简单的例子:
// A simple class with a *public* member
class A
{
public:
int a;
};
// Use private inheritance
class B : private A
{
public:
int b;
};
// Use public inheritance
class C : public A
{
public:
int c;
};
// ...
B my_b;
my_b.a = 0; // Invalid, the member a is private due to the private inhericance
C my_c;
my_c.a = 0; // Valid, because the inheritance is public
private
继承限制了对基类成员的访问。即使A::a
成员变量是public
,由于private
继承它成为private
子类中的B
。
让我们继续看向量的例子。向量只是T
s 的容器。现在假设您要构建一个行为类似于向量的类型,但添加了一些额外的运行时检查。我现在手头没有 TC++PL 的副本,所以我们来做一个约束:例如,假设你的向量只允许包含偶数。尝试插入奇数将导致运行时错误。让我们称这个新类even_vector
和没有运行时检查的版本base_vector
。
even_vector
提供比 更强的运行时保证base_vector
:保证其所有元素都是偶数。
假设您base_vector
的设计目的是作为基类很好地工作(std::vector
通常不会),您现在可能很想使用公共继承来实现even_vector
. base_vector
毕竟,功能是一样的,你只需要even_vector
在base_vector
. 然而,如果你在这里使用公共继承,你将违反里氏替换原则:你不能在even_vector
任何你使用 a 的地方使用 a base_vector
。特别是,even_vector
在您将奇数插入base_vector
. 这很糟糕,因为现在编写的所有代码都base_vector
必须考虑以下事实:base_vector
s 不能处理奇数。
使用私有继承,您就没有这个问题:这里even_vector
继承自的事实base_vector
是实现的细节。客户端不能even_vector
在base_vector
期望的地方使用,所以上面的问题不会发生,但我们仍然得到代码重用的好处。
话虽如此,使用私有继承来重用代码是许多人不鼓励的做法。一个可以说是更好的方法是在这里使用组合,也就是说,添加一个私有base_vector
成员来even_vector
代替。这种方法的优点是它大大减少了两个类之间的耦合,因为even_vector
不再能够访问base_vector
.
让我们看看一个可能的情况,使用接口来帮助构建图片。
class poison {
public:
virtual void used() = 0;
};
class food {
public:
virtual void eat();
protected:
void poisonConsumed(poison& p);
}
class cheese : public food, private poison {
public:
virtual void eat() override {
poisonConsumed(*this);
}
private:
void used() override;
}
这将奶酪向外界展示为“不是毒药”——即班级以外的任何人都无法知道它是毒药,并且可以将其制成“不是毒药”,对任何使用它的东西都没有影响。
然而,奶酪可以将自己传递给任何期待毒药的东西,然后可以自由调用 used(); 即使它在奶酪中是私有的。
如何通过将“接口”限制为基础来定义类?
通过将继承设为私有。当继承为私有时,基类的接口仅限于成员函数,外部不可用。访问说明符可以在基础列表中给出:
class A : private B
// ^^^^^^^
他所说的“更有力的保证”是什么意思?
基础未提供的任何保证,或者是基础提供的保证的超集。
例如,“行为总是明确定义”的保证比“只有当输入不为空时行为才明确定义”要强。另一个例子:“The function does not throw”比“The function will not throw unless the copy constructor throws”强。