继承 — private 和 protected 继承
如何表达“private 继承”?
当您使用 : private 而不是 : public 时。例如:
class Foo : private Bar {
public:
// ...
};
“private 继承”和“组合”有什么相似之处?
private 继承是组合(又名聚合和/或 has-a)的一种语法变体。
例如,“Car has-a Engine”关系可以使用简单组合来表达。
class Engine {
public:
Engine(int numCylinders);
void start(); // Starts this Engine
};
class Car {
public:
Car() : e_(8) { } // Initializes this Car with 8 cylinders
void start() { e_.start(); } // Start this Car by starting its Engine
private:
Engine e_; // Car has-a Engine
};
“Car has-a Engine”关系也可以使用private 继承来表达。
class Car : private Engine { // Car has-a Engine
public:
Car() : Engine(8) { } // Initializes this Car with 8 cylinders
using Engine::start; // Start this Car by starting its Engine
};
这两种变体有几个相似之处:
- 在这两种情况下,每个
Car对象中都恰好包含一个Engine成员对象。 - 在这两种情况下,用户(外部人员)都不能将
Car*转换为Engine*。 - 在这两种情况下,
Car类都有一个start()方法,该方法调用所包含的Engine对象的start()方法。
也有几个区别:
- 如果您想在每辆
Car中包含多个Engine,则需要简单组合变体。 private继承变体可能会引入不必要的多个继承。private继承变体允许Car的成员将Car*转换为Engine*。private继承变体允许访问基类的protected成员。private继承变体允许Car重写Engine的virtual函数。private继承变体使得给Car一个start()方法,该方法简单地调用Engine的start()方法,略微简单一些(20 个字符,而 28 个字符)。
请注意,private 继承通常用于访问基类的 protected 成员,但这通常是一个短期解决方案(翻译:一个权宜之计)。
我应该选择组合还是私有继承?
能用组合时就用组合,必须用 private 继承时才用。
通常,您不希望访问太多其他类的内部,而 private 继承为您提供了这种额外的能力(和责任)。但 private 继承并非邪恶;它只是维护成本更高,因为它增加了某人更改某些内容并破坏您的代码的可能性。
private 继承的一个合法、长期用途是当您想构建一个 class Fred,它使用 class Wilma 中的代码,并且 class Wilma 中的代码需要调用您的新 class Fred 中的成员函数。在这种情况下,Fred 调用 Wilma 中的非 virtual 函数,而 Wilma 调用(通常是纯虚函数)它自己,这些函数由 Fred 重写。这用组合来实现会困难得多。
class Wilma {
protected:
void fredCallsWilma()
{
std::cout << "Wilma::fredCallsWilma()\n";
wilmaCallsFred();
}
virtual void wilmaCallsFred() = 0; // A pure virtual function
};
class Fred : private Wilma {
public:
void barney()
{
std::cout << "Fred::barney()\n";
Wilma::fredCallsWilma();
}
protected:
virtual void wilmaCallsFred()
{
std::cout << "Fred::wilmaCallsFred()\n";
}
};
我应该将私有派生类指针转换为其基类指针吗?
通常,不应该。
从私有派生类的成员函数或友元内部,与基类的关系是已知的,并且从 PrivatelyDer* 到 Base*(或 PrivatelyDer& 到 Base&)的向上转换是安全的;不需要也不建议进行强制转换。
然而,PrivatelyDer 的用户应避免这种不安全的转换,因为它基于 PrivatelyDer 的私有决定,并且可能在不通知的情况下更改。
protected 继承与 private 继承有什么关系?
相似之处:两者都允许重写 private/protected 基类中的virtual 函数,两者都不声明派生类是其基类的一种。
不同之处:protected 继承允许派生类的派生类了解继承关系。因此,您的“孙子”实际上暴露了您的实现细节。这既有好处(它允许 protected 派生类的派生类利用与 protected 基类的关系)也有成本(protected 派生类不能在不潜在破坏进一步派生类的情况下更改关系)。
Protected 继承使用 : protected 语法。
class Car : protected Engine {
public:
// ...
};
private 和 protected 继承的访问规则是什么?
以这些类为例:
class B { /*...*/ };
class D_priv : private B { /*...*/ };
class D_prot : protected B { /*...*/ };
class D_publ : public B { /*...*/ };
class UserClass { B b; /*...*/ };
所有派生类都不能访问 B 中任何 private 的内容。在 D_priv 中,B 的 public 和 protected 部分是 private。在 D_prot 中,B 的 public 和 protected 部分是 protected。在 D_publ 中,B 的 public 部分是 public,B 的 protected 部分是 protected(D_publ 是 B 的一种)。class UserClass 只能访问 B 的 public 部分,这“隔离”了 UserClass 和 B。
要使 B 的 public 成员在 D_priv 或 D_prot 中也为 public,请使用 B:: 前缀声明该成员的名称。例如,要在 D_prot 中使成员 B::f(int,float) 为 public,您可以这样写:
class D_prot : protected B {
public:
using B::f; // Note: Not using B::f(int,float)
};
