什么时候在构造函数中调用虚函数是安全的
我有一些代码,我真的想从构造函数调用虚拟方法。我知道这被认为是不安全的,并且我对对象构造有足够的了解,也可以理解 为什么。我也没有遇到这些问题。目前我的代码正在运行,我认为它应该没问题,但我想确定一下。
这就是我正在做的:
我有一些类层次结构,并且有一个正常的公共函数,它像往常一样转发到私有虚拟方法。但是,我确实想在构造对象时调用此公共方法,因为它将所有数据填充到对象中。我将绝对确定这个虚拟调用来自叶类,因为从类层次结构的任何其他部分使用这个虚拟方法根本没有意义。
所以在我看来,一旦我进行虚拟调用,对象创建就应该完成,一切都应该没问题。还有什么可能出错的地方吗?我想我必须用一些大注释来标记这部分逻辑,以解释为什么这个逻辑永远不应该移动到任何基类,即使它看起来可以移动。但除了其他程序员的愚蠢之外,我应该没问题,不是吗?
I have some code where I really want to call a virtual method from a constructor. I know this is considered unsafe, and I know enough about object construction to also understand why. I also am not experiencing these problems. Currently my code is working and I think it should be fine, but I want to make sure.
Here is what I am doing:
I have some class hierarchy and there is a normal public function which just forwards to a private virtual method, as usual. However I do want to call this public method upon construction of my objects, because it is filling all data into the object. I will be absolutely sure that this virtual call comes from the leaf class, because using this virtual method from any other part of the class hierarchy simply does not make sense at all.
So in my opinion the object creation should be finished once I am doing the virtual call and everything should be fine. Is there still anything that could go wrong? I guess I'll have to mark this part of the logic with some big comments to explain why this logic should never ever be moved to any of the base clases, even though it looks like it could be moved. But other than stupidity of other programmers I should be fine, shouldn't I?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
在构造函数或析构函数中调用任何非抽象虚函数是绝对安全的!然而,它的行为可能会令人困惑,因为它可能不会达到预期的效果。当执行类的构造函数时,对象的静态和动态类型就是构造函数的类型。也就是说,虚拟函数将永远被分派给进一步派生类的重写。除此之外,虚拟分派实际上是有效的:例如,当通过基类指针或引用调用虚拟函数时,正确分派到当前正在构造或析构的类中的覆盖。例如(可能充满拼写错误,因为我目前无法编写此代码):
也就是说,如果您不希望虚拟函数被分派到进一步的派生函数。您甚至可以这样做,只要定义了虚函数,它在给定类中就是抽象的。但是,分派未定义的抽象函数将导致运行时错误。
It is absolutely safe to call any non-abstract virtual function in the constructor or the destructor! However, its behavior may be confusing as it may not do what is expected. While the constructor of a class is executed, the static and dynamic type of the object is the type of the constructor. That is, the virtual function will never be dispatched to the override of a further derived class. Other than that, virtual dispatch actually works: e.g. when calling a virtual function via a base class pointer or reference correctly dispatches to the override in the class being currently constructor or destructed. For example (probably riddled with typos as I currently can't this code):
That is, you can call a virtual function, directly or indirectly, in the constructor or a destructor of a class if you don't want the virtual function to be dispatched to a further derived function. You can even do this is virtual function is abstract in the given class as long as it is defined. However, having an undefined abstract function being dispatched to will cause a run-time error.
当调用构造函数时,该类将被设置为该类的实例,而不是派生类。您不能从基构造函数调用派生类的虚函数。当您到达最派生类的构造函数时,所有虚函数都应该可以安全调用。
如果您希望确保某人不会进行错误的调用,请在基类中定义虚拟函数,并在调用它时让它断言和/或抛出异常。
When a constructor is called, the class is set up to be an instance of that class but not the derived class. You cannot call into a virtual function of a derived class from a base constructor. By the time you get to the constructor of the most derived class, all of the virtual functions should be safe to call.
If you wish to ensure that someone can't make an incorrect call, define the virtual function in the base class and have it assert and/or throw an exception when it is called.
该规则并不是说您需要位于叶类中,而是要意识到当您从
Foo::Foo(..)
进行成员调用时,该对象正是一个Foo
,即使它正在成为Bar
(假设Foo
派生自Bar
并且您正在构造一个Bar
实例)。这是100%可靠的。否则,成员是虚拟的这一事实并不那么重要。非虚拟函数也会出现其他陷阱:如果您要调用虚拟或非虚拟方法,该方法假设对象已完全构造,但在此之前在构造函数中调用了它,那么您也会有问题。这些只是很难确定的情况,因为不仅您调用的函数必须没问题,它调用的所有函数也必须没问题。
听起来您没有遇到问题,这只是容易出现错误的地方之一。
The rule isn't so much that you need to be in a leaf class as to realize that when you make a member call from
Foo::Foo(..)
, the object is exactly aFoo
, even if it's on its way to being aBar
(assumingFoo
is derived fromBar
and you're constructing aBar
instance). That's 100% reliable.Otherwise, the fact that the member is virtual isn't all that significant. There are other pitfalls that happen just as well with non-virtual functions: if you were to call a virtual or non-virtual method that assumed the object was completely constructed but called it within the constructor before that was the case, you'd also have problems. These are just hard cases to pin down because not only must the function you call be okay, all the functions it calls must be okay.
It doesn't sound like you have a problem, it's just one of those places prone for errors to crop up.