C++ 单例类——继承的好习惯
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9370749/
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
C++ Singleton class - inheritance good practice
提问by kiriloff
In an existing project, I am to inherit a Controller class (MVC) declared as Singleton so as to define my own treatment. How to appropriately derive this Singleton class?
在现有的项目中,我要继承一个声明为Singleton的Controller类(MVC),以便定义我自己的处理方式。如何适当地派生这个 Singleton 类?
First, I expand on context and need for this inheritance.
首先,我扩展了上下文和这种继承的需要。
The application that I am added to the existing software wants to use a MVC module that performs almost same task as the one I am willing to perform. It is using the same methods up to signature and slight modifications. Rewriting my own MVC module would definitively be duplication of code. The existing module is intrinsically oriented towards its application to another part of the software, and I cannot simply use the same module. But is written as a Model-View-Controller pattern where Controller is Singleton. I derived View already.
我添加到现有软件中的应用程序想要使用一个 MVC 模块,该模块执行的任务与我愿意执行的任务几乎相同。它使用相同的方法进行签名和轻微修改。重写我自己的 MVC 模块肯定会重复代码。现有模块本质上是面向其应用到软件的另一部分,我不能简单地使用相同的模块。但是被写成模型-视图-控制器模式,其中控制器是单例。我已经派生了 View。
Second, I have doubt that I can classicaly derive Singleton class.
其次,我怀疑我可以经典地派生 Singleton 类。
Calling constructor from inherited class would simply call getinstance() for parent class and fail to return an object from derived class (?).
从继承类调用构造函数只会为父类调用 getinstance() 并且无法从派生类 (?) 返回对象。
Third, it's how I see some way to deal with. Please comment/help me improve!
第三,这就是我所看到的一些处理方式。请评论/帮助我改进!
I copy the whole Singleton class in a class I could call AbstractController. I derive this class twice. The first child is singleton and adopts the whole treatment of parent class. The second child is the Controller for my part of the application, with own redefined treatment.
我将整个 Singleton 类复制到一个可以称为 AbstractController 的类中。我两次派生这个类。第一个孩子是单身,采用父类的整体处理。第二个孩子是我的应用程序部分的控制器,具有自己重新定义的处理方式。
Thanks!
谢谢!
采纳答案by je4d
I'm not sure I understand the situation you're dealing with fully, and whether or not it's possible or appropriate to derive from the singleton depends very much on how the singleton is implemented.
我不确定我是否完全理解您正在处理的情况,从单例派生是否可能或合适在很大程度上取决于单例的实现方式。
But since you mentioned "good practice" there's some general points that come to mind when reading the question:
但是既然你提到了“良好的实践”,那么在阅读这个问题时会想到一些一般性的观点:
Inheritance isn't usually the best tool to achieve code re-use. See: Prefer composition over inheritance?
Using singleton and "good practice" generally do not go together! See: What is so bad about singletons?
继承通常不是实现代码重用的最佳工具。请参阅:更喜欢组合而不是继承?
使用单例和“良好实践”通常不会一起使用!请参阅:单身人士有什么不好?
Hope that helps.
希望有帮助。
回答by cHao
Truth is, singletons and inheritance do not play well together.
事实是,单例和继承不能很好地结合在一起。
Yeah, yeah, the Singleton lovers and GoF cult will be all over me for this, saying "well, if you make your constructor protected..." and "you don't haveto have a getInstance
method on the class, you can put it...", but they're just proving my point. Singletons have to jump through a number of hoops in order to be both a singleton and a base class.
是啊,是啊,辛格尔顿爱好者和GoF的崇拜将是对我的一切对于这一点,说:“好了,如果你把你的构造保护...”和“你不具备有getInstance
对类的方法,你可以把它......”,但它们只是证明了我的观点。单身人士必须跳过许多障碍才能成为单身人士和基类。
But just to answer the question, say we have a singleton base class. It can even to some degree enforce its singleness through inheritance. (The constructor does one of the few things that can work when it can no longer be private: it throws an exception if another Base
already exists.) Say we also have a class Derived
that inherits from Base
. Since we're allowing inheritance, let's also say there can be any number of other subclasses of Base
, that may or may not inherit from Derived
.
但只是为了回答这个问题,假设我们有一个单例基类。它甚至可以在某种程度上通过继承来加强其单一性。(当构造函数不再是私有的时,它会做为数不多的可以工作的事情之一:如果另一个Base
已经存在,它会抛出一个异常。)假设我们还有一个Derived
继承自Base
. 由于我们允许继承,因此我们也可以说 可以有任意数量的其他子类Base
,它们可能继承也可能不继承自Derived
。
But there's a problem -- the very one you're either already running into, or will soon. If we call Base::getInstance
without having constructed an object already, we'll get a null pointer. We'd like to get back whatever singleton object exists (it may be a Base
, and/or a Derived
, and/or an Other
). But it's hard to do so and still follow all the rules, cause there are only a couple of ways to do so -- and all of them have some drawbacks.
但是有一个问题——你要么已经遇到了,要么很快就会遇到这个问题。如果我们在Base::getInstance
没有构造对象的情况下调用,我们将得到一个空指针。我们想取回任何存在的单例对象(它可能是 a Base
,和/或 a Derived
,和/或 an Other
)。但是很难做到这一点并且仍然遵守所有规则,因为只有几种方法可以做到——而且所有方法都有一些缺点。
We could just create a
Base
and return it. ScrewDerived
andOther
. End result:Base::getInstance()
always returns exactly aBase
. The child classes never get to play. Kinda defeats the purpose, IMO.We could put a
getInstance
of our own in our derived class, and have the caller sayDerived::getInstance()
if they specifically want aDerived
. This significantly increases coupling (because a caller now has to know to specifically request aDerived
, and ends up tying itself to that implementation).We could do a variant of that last one -- but instead of getting the instance, the function just creates one. (While we're at it, let's rename the function to
initInstance
, since we don't particularly care what it gets -- we're just calling it so that it creates a newDerived
and sets that as the One True Instance.)
我们可以创建一个
Base
并返回它。螺丝Derived
和Other
。最终结果:Base::getInstance()
始终准确返回 aBase
。儿童班从来没有玩过。有点违背目的,IMO。我们可以
getInstance
在我们的派生类中放入我们自己的a ,并让调用者说Derived::getInstance()
他们是否特别想要一个Derived
. 这显着增加了耦合(因为调用者现在必须知道具体请求 aDerived
,并最终将自己绑定到该实现)。我们可以做最后一个的变体——但不是获取实例,函数只是创建一个。(在此期间,让我们将函数重命名为
initInstance
,因为我们并不特别关心它得到了什么——我们只是调用它以便它创建一个新Derived
实例并将其设置为 One True 实例。)
So (barring any oddness unaccounted for yet), it works out kinda like this...
所以(除非有任何未知的奇怪之处),它有点像这样......
class Base {
static Base * theOneTrueInstance;
public:
static Base & getInstance() {
if (!theOneTrueInstance) initInstance();
return *theOneTrueInstance;
}
static void initInstance() { new Base; }
protected:
Base() {
if (theOneTrueInstance) throw std::logic_error("Instance already exists");
theOneTrueInstance = this;
}
virtual ~Base() { } // so random strangers can't delete me
};
Base* Base::theOneTrueInstance = 0;
class Derived : public Base {
public:
static void initInstance() {
new Derived; // Derived() calls Base(), which sets this as "the instance"
}
protected:
Derived() { } // so we can't be instantiated by outsiders
~Derived() { } // so random strangers can't delete me
};
And in your init code, you say Base::initInstance();
or Derived::initInstance();
, depending on which type you want the singleton to be. You'll have to cast the return value from Base::getInstance()
in order to use any Derived
-specific functions, of course, but without casting you can use any functions defined by Base
, including virtual functions overridden by Derived
.
在您的 init 代码中,您说Base::initInstance();
或Derived::initInstance();
,具体取决于您希望单例是哪种类型。当然,您必须转换返回值 fromBase::getInstance()
才能使用任何Derived
特定的函数,但是如果不转换,您可以使用由 定义的任何函数Base
,包括由 覆盖的虚函数Derived
。
Note that this way of doing it also has a number of drawbacks of its own, though:
请注意,这种做法也有其自身的许多缺点:
It puts most of the burden of enforcing singleness on the base class. If the base doesn't have this or similar functionality, and you can't change it, you're kinda screwed.
The base class can't take allof the responsibility, though -- each class needs to declare a protected destructor, or someone could come along and delete the one instance after casting it (in)appropriately, and the whole thing goes to hell. What's worse, this can't be enforced by the compiler.
Because we're using protected destructors to prevent some random schmuck from deleting our instance, unless the compiler's smarter than i fear it is, even the runtime won't be able to properly delete your instance when the program ends. Bye bye, RAII...hello "memory leak detected" warnings. (Of course the memory will eventually be reclaimed by any decent OS. But if the destructor doesn't run, you can't depend on it to do cleanup for you. You'll need to call a cleanup function of some sort before you exit, and that won't give you anywhere near the same assurances that RAII can give you.)
It exposes an
initInstance
method that, IMO, doesn't really belong in an API everyone can see. If you wanted, you could makeinitInstance
private and let your init function be afriend
, but then your class is making assumptions about code outside itself, and the coupling thing comes back into play.
它将强制单身的大部分负担放在基类上。如果底座没有这个或类似的功能,而且你不能改变它,那你就有点搞砸了。
然而,基类不能承担所有的责任——每个类都需要声明一个受保护的析构函数,或者有人可以在适当地(在)强制转换后删除一个实例,然后整个事情就变得很糟糕了。更糟糕的是,这不能由编译器强制执行。
因为我们使用受保护的析构函数来防止一些随机的傻瓜删除我们的实例,除非编译器比我担心的更聪明,否则即使运行时在程序结束时也无法正确删除您的实例。再见,RAII...你好“检测到内存泄漏”警告。(当然,内存最终会被任何体面的操作系统回收。但是如果析构函数没有运行,你就不能依赖它为你做清理。你需要在你之前调用某种清理函数退出,这不会给你任何接近 RAII 可以给你的保证。)
它公开了一种
initInstance
方法,IMO 并不真正属于每个人都可以看到的 API。如果您愿意,您可以initInstance
设为私有并让您的 init 函数成为 afriend
,但是您的类正在对自身外部的代码做出假设,并且耦合的事情又开始发挥作用。
Also note that the code above is not at all thread safe. If you need that, you're on your own.
还要注意,上面的代码根本不是线程安全的。如果你需要那个,你就靠你自己了。
Seriously, the less painful route is to forget trying to enforce singleness. The least complicated way to ensure that there's only one instance is to only createone. If you need to use it multiple places, consider dependency injection. (The non-framework version of that amounts to "pass the object to stuff that needs it". :P ) I went and designed the above stuff just to try and prove myself wrong about singletons and inheritance, and just reaffirmed to myself how evil the combination is. I wouldn't recommend ever actually doing it in real code.
说真的,不那么痛苦的方法是忘记尝试强制单身。确保只有一个实例的最简单的方法是只创建一个。如果需要多处使用,可以考虑依赖注入。(非框架版本相当于“将对象传递给需要它的东西”。:P)我去设计了上面的东西只是为了尝试证明自己在单例和继承方面是错误的,只是向自己重申了多么邪恶组合是。我不建议在真正的代码中真正做到这一点。