在基类构造函数中调用虚方法

Yon*_*ona 32 c# c++ java constructor

我知道,从基类的构造函数调用虚方法可能是危险的,因为子类可能不处于有效状态.(至少在C#中)

我的问题是如果虚拟方法是初始化对象状态的那个?它是很好的做法,还是应该是两个步骤,首先创建对象,然后加载状态?

第一个选项:(使用构造函数初始化状态)

public class BaseObject {
    public BaseObject(XElement definition) {
        this.LoadState(definition);
    }

    protected abstract LoadState(XElement definition);
}
Run Code Online (Sandbox Code Playgroud)

第二种选择:(使用两步法)

public class BaseObject {
    public void LoadState(XElement definition) {
        this.LoadStateCore(definition);
    }

    protected abstract LoadStateCore(XElement definition);
}
Run Code Online (Sandbox Code Playgroud)

在第一种方法中,代码的使用者可以使用一个语句创建和初始化对象:

// The base class will call the virtual method to load the state.
ChildObject o = new ChildObject(definition)
Run Code Online (Sandbox Code Playgroud)

在第二种方法中,消费者必须创建对象然后加载状态:

ChildObject o = new ChildObject();
o.LoadState(definition);
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 39

(这个答案适用于C#和Java.我相信C++在这个问题上的工作方式不同.)

在构造函数中调用虚方法确实很危险,但有时它最终会得到最干净的代码.

我会尽量避免在可能的情况,但没有弯曲的设计巨大.(例如,"后来初始化"选项禁止不变性.)如果你在构造函数中使用虚拟方法,将其记录下来非常强烈.只要所涉及的每个人都知道它在做什么,就不应该造成太多问题.我会尝试限制可见性,就像你在第一个例子中所做的那样.

编辑:这里重要的一件事是C#和Java之间的初始化顺序有所不同.如果你有一个类,如:

public class Child : Parent
{
    private int foo = 10;

    protected override void ShowFoo()
    {
        Console.WriteLine(foo);
    }
}
Run Code Online (Sandbox Code Playgroud)

其中Parent构造函数调用ShowFoo,在C#,将显示10 Java中的等效方案将显示0.

  • Downvoters:如果您认为我错了,请添加评论!我也想学习...... (17认同)

Gre*_*ers 10

在C++中,在基类构造函数中调用虚方法只会调用该方法,就好像派生类还不存在一样(因为它没有).这意味着调用在编译时被解析为它应该在基类(或它派生自的类)中调用的任何方法.

通过GCC测试,它允许您从构造函数调用纯虚函数,但它会发出警告,并导致链接时错误.标准似乎未定义此行为:

"可以从抽象类的构造函数(或析构函数)调用成员函数; 直接或间接地对从这样的创建(或销毁)的对象进行虚拟调用(class.virtual)到纯虚函数的效果构造函数(或析构函数)未定义."