Java 构造函数中的虚函数,为什么语言不同? 在C++中,当从构造函数中调用虚函数时,它的行为不象虚函数。

Java 构造函数中的虚函数,为什么语言不同? 在C++中,当从构造函数中调用虚函数时,它的行为不象虚函数。,java,.net,c++,language-agnostic,Java,.net,C++,Language Agnostic,我认为第一次遇到这种行为的每个人都感到惊讶,但第二次认为这是有道理的: 只要派生构造函数尚未执行,对象就不是派生实例 那么如何调用派生函数呢?前提条件还没有建立起来。例如: class base { public: base() { std::cout << "foo is " << foo() << std::endl; } virtual int foo() { return 42; } }; class

我认为第一次遇到这种行为的每个人都感到惊讶,但第二次认为这是有道理的:

只要派生构造函数尚未执行,对象就不是派生实例

那么如何调用派生函数呢?前提条件还没有建立起来。例如:

class base {
public:
    base()
    {
        std::cout << "foo is " << foo() << std::endl;
    }
    virtual int foo() { return 42; }
};

class derived : public base {
    int* ptr_;
public:
    derived(int i) : ptr_(new int(i*i)) { }
    // The following cannot be called before derived::derived due to how C++ behaves, 
    // if it was possible... Kaboom!
    virtual int foo()   { return *ptr_; } 
};
类基{
公众:
base()
{

std::cout这两种方法都可能导致意外的结果。最好的办法是根本不要在构造函数中调用虚函数


<> P> C++的方式是比较有意义的,但是当有人查看代码时会导致预期问题。如果你意识到这种情况,你就不应该把代码放在这种情况下,以便以后调试。

< P>我认为C++提供了最好的语义,具有最正确的行为……但是它是更多的工作F。或者编译器和代码对于以后阅读它的人来说绝对是不直观的

使用.NET方法时,函数必须非常有限,不能依赖任何派生对象状态

构造函数中的虚函数,为什么语言不同

< P>因为没有一个好的行为。我发现C++的行为更有意义(因为基类C-Tor被称为第一个,所以它应该调用基类虚拟函数——毕竟,派生类C-Tor还没有运行,所以它可能没有为派生类虚函数设置正确的先决条件)。
但有时,如果我想使用虚拟函数来初始化状态(因此在未初始化状态下调用它们并不重要),C#/Java行为会更好。

Delphi在VCL GUI框架中充分利用虚拟构造函数:

type
  TComponent = class
  public
    constructor Create(AOwner: TComponent); virtual; // virtual constructor
  end;

  TMyEdit = class(TComponent)
  public
    constructor Create(AOwner: TComponent); override; // override virtual constructor
  end;

  TMyButton = class(TComponent)
  public
    constructor Create(AOwner: TComponent); override; // override virtual constructor
  end;

  TComponentClass = class of TComponent;

function CreateAComponent(ComponentClass: TComponentClass; AOwner: TComponent): TComponent;
begin
  Result := ComponentClass.Create(AOwner);
end;

var
  MyEdit: TMyEdit;
  MyButton: TMyButton;
begin
  MyEdit := CreateAComponent(TMyEdit, Form) as TMyEdit;
  MyButton := CreateAComponent(TMyButton, Form) as TMyButton;
end;

语言定义对象生命周期的方式有一个根本的区别。在Java和.Net中,在运行任何构造函数之前,对象成员都是零/空初始化的,此时对象生命周期开始。因此,当您输入构造函数时,您已经得到了一个初始化的对象

<>在C++中,对象生命期只在构造函数完成时开始(虽然成员变量和基类在它开始之前完全构造)。这解释了调用虚函数时的行为,以及构造函数体中存在异常时,为什么不运行析构函数。

对象生命周期的Java/net定义的问题是,要确保对象总是满足其不变量,而不必在初始化对象但构造函数尚未运行时必须设置特殊情况。C++定义的问题是,在java对象的一个奇数周期内,对象处于边缘而不是FU。LLY构造。

我发现C++行为非常讨厌。不能编写虚拟函数,例如返回对象的期望大小,默认构造函数初始化每个项目。例如,很好地做:

BaseClass(){

对于(int i=0;i)来说,编译器需要做的更多工作是什么?它可以归结为在调用基类构造函数后设置vptr。我想说的是,其他语义更难实现,因为您需要确保在派生类构造函数中设置vptr后,基类构造函数不能覆盖它。(假设动态调度是通过指向虚拟方法表的指针来处理的,这是最常见的方法)。@ LuxToule非常迟加1,这是非常正确的。C++方法更干净,更直接。