我什么时候应该使用 C++ 私有继承?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/656224/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When should I use C++ private inheritance?
提问by fizzer
Unlike protected inheritance, C++ private inheritance found its way into mainstream C++ development. However, I still haven't found a good use for it.
与受保护的继承不同,C++ 私有继承进入了主流 C++ 开发。但是,我仍然没有找到它的好用处。
When do you guys use it?
大家什么时候用啊?
采纳答案by David Rodríguez - dribeas
Note after answer acceptance: This is NOT a complete answer. Read other answers like here(conceptually) and here(both theoretic and practic) if you are interested in the question. This is just a fancy trick that can be achieved with private inheritance. While it isfancy it is not the answer to the question.
回答接受后注意:这不是一个完整的答案。如果您对这个问题感兴趣,请阅读此处(概念上)和此处(理论和实践)等其他答案。这只是可以通过私有继承实现的奇特技巧。虽然它很花哨,但它不是问题的答案。
Besides the basic usage of just private inheritance shown in the C++ FAQ (linked in other's comments) you can use a combination of private and virtual inheritance to seala class (in .NET terminology) or to make a class final(in Java terminology). This is not a common use, but anyway I found it interesting:
除了 C++ FAQ(在其他人的评论中链接)中显示的仅私有继承的基本用法之外,您还可以使用私有和虚拟继承的组合来密封类(在 .NET 术语中)或使类成为final(在 Java 术语中) . 这不是一个常见的用途,但无论如何我觉得它很有趣:
class ClassSealer {
private:
friend class Sealed;
ClassSealer() {}
};
class Sealed : private virtual ClassSealer
{
// ...
};
class FailsToDerive : public Sealed
{
// Cannot be instantiated
};
Sealedcan be instantiated. It derives from ClassSealerand can call the private constructor directly as it is a friend.
Sealed可以实例化。它派生自ClassSealer,可以像朋友一样直接调用私有构造函数。
FailsToDerivewon't compile as it must call the ClassSealerconstructor directly (virtual inheritance requirement), but it cannot as it is private in the Sealedclass and in this case FailsToDeriveis not a friend of ClassSealer.
FailsToDerive将无法编译,因为它必须调用ClassSealer直接构造(虚拟继承的要求),但它不能,因为它是在私人密封类,并在这种情况下FailsToDerive不是朋友ClassSealer。
EDIT
编辑
It was mentioned in the comments that this could not be made generic at the time using CRTP. The C++11 standard removes that limitation by providing a different syntax to befriend template arguments:
评论中提到,在使用 CRTP 时,这无法通用。C++11 标准通过提供不同的语法来帮助模板参数消除该限制:
template <typename T>
class Seal {
friend T; // not: friend class T!!!
Seal() {}
};
class Sealed : private virtual Seal<Sealed> // ...
Of course this is all moot, since C++11 provides a final
contextual keyword for exactly this purpose:
当然,这一切都没有实际意义,因为 C++11final
正是为此目的提供了一个上下文关键字:
class Sealed final // ...
回答by fizzer
I use it all the time. A few examples off the top of my head:
我用它所有的时间。我脑子里有几个例子:
- When I want to expose some but not all of a base class's interface. Public inheritance would be a lie, as Liskov substitutabilityis broken, whereas composition would mean writing a bunch of forwarding functions.
- When I want to derive from a concrete class without a virtual destructor. Public inheritance would invite clients to delete through a pointer-to-base, invoking undefined behaviour.
- 当我想公开一些但不是全部基类的接口时。公共继承将是一个谎言,因为Liskov 可替代性被破坏了,而组合则意味着编写一堆转发函数。
- 当我想从没有虚拟析构函数的具体类派生时。公共继承将邀请客户端通过指向基的指针进行删除,从而调用未定义的行为。
A typical example is deriving privately from an STL container:
一个典型的例子是从 STL 容器私下派生:
class MyVector : private vector<int>
{
public:
// Using declarations expose the few functions my clients need
// without a load of forwarding functions.
using vector<int>::push_back;
// etc...
};
- When implementing the Adapter Pattern, inheriting privately from the Adapted class saves having to forward to an enclosed instance.
- To implement a private interface. This comes up often with the Observer Pattern. Typically my Observer class, MyClass say, subscribes itselfwith some Subject. Then, only MyClass needs to do the MyClass -> Observer conversion. The rest of the system doesn't need to know about it, so private inheritance is indicated.
- 在实现适配器模式时,从 Adapted 类私下继承可以避免转发到封闭的实例。
- 实现私有接口。这经常出现在观察者模式中。通常我的观察者类,MyClass 说,用一些主题订阅自己。那么,只有MyClass 需要做MyClass -> Observer 的转换。系统的其余部分不需要知道它,因此指示了私有继承。
回答by Harper Shelby
The canonical usage of private inheritance is the "implemented in terms of" relationship (thanks to Scott Meyers' 'Effective C++' for this wording). In other words, the external interface of the inheriting class has no (visible) relationship to the inherited class, but it uses it internally to implement its functionality.
私有继承的规范用法是“根据”关系实现(感谢 Scott Meyers 的“Effective C++”这个措辞)。换句话说,继承类的外部接口与继承类没有(可见的)关系,而是在内部使用它来实现其功能。
回答by Daemin
One useful use of private inheritence is when you have a class that implements an interface, that is then registered with some other object. You make that interface private so that the class itself has to register and only the specific object that its registered with can use those functions.
私有继承的一个有用用途是当您有一个实现接口的类时,然后将其注册到某个其他对象。您将该接口设为私有,以便类本身必须注册,并且只有其注册的特定对象才能使用这些函数。
For example:
例如:
class FooInterface
{
public:
virtual void DoSomething() = 0;
};
class FooUser
{
public:
bool RegisterFooInterface(FooInterface* aInterface);
};
class FooImplementer : private FooInterface
{
public:
explicit FooImplementer(FooUser& aUser)
{
aUser.RegisterFooInterface(this);
}
private:
virtual void DoSomething() { ... }
};
Therefore the FooUser class can call the private methods of FooImplementer through the FooInterface interface, while other external classes cannot. This is a great pattern for handling specific callbacks that are defined as interfaces.
因此 FooUser 类可以通过 FooInterface 接口调用 FooImplementer 的私有方法,而其他外部类则不能。这是处理定义为接口的特定回调的绝佳模式。
回答by Bill the Lizard
I think the critical section from the C++ FAQ Liteis:
我认为C++ FAQ Lite 中的关键部分是:
A legitimate, long-term use for private inheritance is when you want to build a class Fred that uses code in a class Wilma, and the code from class Wilma needs to invoke member functions from your new class, Fred. In this case, Fred calls non-virtuals in Wilma, and Wilma calls (usually pure virtuals) in itself, which are overridden by Fred. This would be much harder to do with composition.
私有继承的合法、长期用途是当您想要构建使用类 Wilma 中的代码的 Fred 类,并且类 Wilma 中的代码需要调用新类 Fred 中的成员函数时。在这种情况下,Fred 调用 Wilma 中的非虚拟对象,而 Wilma 调用(通常是纯虚拟对象)本身,它们被 Fred 覆盖。这将很难用组合来完成。
If in doubt, you should prefer composition over private inheritance.
如果有疑问,您应该更喜欢组合而不是私有继承。
回答by bias
I find it useful for interfaces (viz. abstract classes) that I'm inheriting where I don't want other code to touch the interface (only the inheriting class).
我发现它对我继承的接口(即抽象类)很有用,我不希望其他代码触及接口(仅继承类)。
[edited in an example]
[在一个例子中编辑]
Take the examplelinked to above. Saying that
以上面链接的例子为例。这么说
[...] class Wilma needs to invoke member functions from your new class, Fred.
[...] 类 Wilma 需要从您的新类 Fred 调用成员函数。
is to say that Wilma is requiring Fred to be able to invoke certain member functions, or, rather it is saying that Wilma is an interface. Hence, as mentioned in the example
是说 Wilma 要求 Fred 能够调用某些成员函数,或者更确切地说,是说Wilma 是一个接口。因此,如示例中所述
private inheritance isn't evil; it's just more expensive to maintain, since it increases the probability that someone will change something that will break your code.
私有继承并不邪恶;它只是维护成本更高,因为它增加了有人更改会破坏您代码的内容的可能性。
comments on the desired effect of programmers needing to meet our interface requirements, or breaking the code. And, since fredCallsWilma() is protected only friends and derived classes can touch it i.e. an inherited interface (abstract class) that only the inheriting class can touch (and friends).
对程序员需要满足我们的接口要求或破坏代码的预期效果的评论。并且,由于 fredCallsWilma() 只有朋友和派生类可以接触它,即只有继承类可以接触(和朋友)的继承接口(抽象类)。
[edited in another example]
[在另一个例子中编辑]
This pagebriefly discusses private interfaces (from yet another angle).
本页简要讨论了私有接口(从另一个角度)。
回答by bias
Sometimes I find it useful to use private inheritance when I want to expose a smaller interface (e.g. a collection) in the interface of another, where the collection implementation requires access to the state of the exposing class, in a similar manner to inner classes in Java.
有时,当我想在另一个接口中公开一个较小的接口(例如一个集合)时,我发现使用私有继承很有用,其中集合实现需要访问公开类的状态,类似于内部类爪哇。
class BigClass;
struct SomeCollection
{
iterator begin();
iterator end();
};
class BigClass : private SomeCollection
{
friend struct SomeCollection;
SomeCollection &GetThings() { return *this; }
};
Then if SomeCollection needs to access BigClass, it can static_cast<BigClass *>(this)
. No need to have an extra data member taking up space.
那么如果 SomeCollection 需要访问 BigClass,它可以static_cast<BigClass *>(this)
。无需额外的数据成员占用空间。
回答by Matth?us Brandl
I found a nice application for private inheritance, although it has a limited usage.
我发现了一个很好的私有继承应用程序,尽管它的使用有限。
Problem to solve
要解决的问题
Suppose you are given the following C API:
假设您有以下 C API:
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
/* raw owning pointer, it's C after all */
char const * name;
/* more variables that need resources
* ...
*/
} Widget;
Widget const * loadWidget();
void freeWidget(Widget const * widget);
#ifdef __cplusplus
} // end of extern "C"
#endif
Now your job is to implement this API using C++.
现在您的工作是使用 C++ 实现这个 API。
C-ish approach
C-ish方法
Of course we could choose a C-ish implementation style like so:
当然,我们可以选择像这样的 C-ish 实现风格:
Widget const * loadWidget()
{
auto result = std::make_unique<Widget>();
result->name = strdup("The Widget name");
// More similar assignments here
return result.release();
}
void freeWidget(Widget const * const widget)
{
free(result->name);
// More similar manual freeing of resources
delete widget;
}
But there are several disadvantages:
但是有几个缺点:
- Manual resource (e.g. memory) management
- It is easy to set up the
struct
wrong - It is easy to forget freeing the resources when freeing the
struct
- It is C-ish
- 手动资源(例如内存)管理
- 很容易设置
struct
错误 - 释放资源时很容易忘记释放资源
struct
- 这是C-ish
C++ Approach
C++ 方法
We are allowed to use C++, so why not use its full powers?
我们可以使用 C++,那么为什么不使用它的全部功能呢?
Introducing automated resource management
引入自动化资源管理
The above problems are basically all tied to the manual resource management. The solution that comes to mind is to inherit from Widget
and add a resource managing instance to the derived class WidgetImpl
for each variable:
以上问题基本上都与人工资源管理有关。想到的解决方案是从每个变量Widget
的派生类继承并添加一个资源管理实例WidgetImpl
:
class WidgetImpl : public Widget
{
public:
// Added bonus, Widget's members get default initialized
WidgetImpl()
: Widget()
{}
void setName(std::string newName)
{
m_nameResource = std::move(newName);
name = m_nameResource.c_str();
}
// More similar setters to follow
private:
std::string m_nameResource;
};
This simplifies the implementation to the following:
这将实现简化为以下内容:
Widget const * loadWidget()
{
auto result = std::make_unique<WidgetImpl>();
result->setName("The Widget name");
// More similar setters here
return result.release();
}
void freeWidget(Widget const * const widget)
{
// No virtual destructor in the base class, thus static_cast must be used
delete static_cast<WidgetImpl const *>(widget);
}
Like this we remedied all the above problems. But a client can still forget about the setters of WidgetImpl
and assign to the Widget
members directly.
这样我们就解决了上述所有问题。但是客户端仍然可以忘记直接WidgetImpl
分配给Widget
成员的 setter 。
Private inheritance enters the stage
私有继承上台
To encapsulate the Widget
members we use private inheritance. Sadly we now need two extra functions to cast between both classes:
为了封装Widget
成员,我们使用私有继承。遗憾的是,我们现在需要两个额外的函数来在两个类之间进行转换:
class WidgetImpl : private Widget
{
public:
WidgetImpl()
: Widget()
{}
void setName(std::string newName)
{
m_nameResource = std::move(newName);
name = m_nameResource.c_str();
}
// More similar setters to follow
Widget const * toWidget() const
{
return static_cast<Widget const *>(this);
}
static void deleteWidget(Widget const * const widget)
{
delete static_cast<WidgetImpl const *>(widget);
}
private:
std::string m_nameResource;
};
This makes the following adaptions necessary:
这使得以下调整成为必要:
Widget const * loadWidget()
{
auto widgetImpl = std::make_unique<WidgetImpl>();
widgetImpl->setName("The Widget name");
// More similar setters here
auto const result = widgetImpl->toWidget();
widgetImpl.release();
return result;
}
void freeWidget(Widget const * const widget)
{
WidgetImpl::deleteWidget(widget);
}
This solution solves all the problems. No manual memory management and Widget
is nicely encapsulated so that WidgetImpl
does not have any public data members anymore. It makes the implementation easy to use correctly and hard (impossible?) to use wrong.
该解决方案解决了所有问题。没有手动内存管理,并且Widget
被很好地封装,因此WidgetImpl
不再有任何公共数据成员。它使实现易于正确使用而难以(不可能?)使用错误。
The code snippets form a compiling example on Coliru.
回答by sportswithin.com
If derived class - needs to reuse code and - you can't change base class and - is protecting its methods using base's members under a lock.
如果派生类 - 需要重用代码并且 - 您不能更改基类并且 - 正在使用锁定的基类成员保护其方法。
then you should use private inheritance, otherwise you have danger of unlocked base methods exported via this derived class.
那么你应该使用私有继承,否则你有通过这个派生类导出的解锁基方法的危险。
回答by bayda
Sometimes it could be an alternative to aggregation, for example if you want aggregation but with changed behaviour of aggregable entity (overriding the virtual functions).
有时它可能是aggregation的替代方案,例如,如果您想要聚合但更改了可聚合实体的行为(覆盖虚函数)。
But you're right, it has not many examples from the real world.
但你是对的,它没有太多来自现实世界的例子。