59

为什么下面的示例打印“0”以及必须更改什么才能按我的预期打印“1”?

#include <iostream>
struct base {
   virtual const int value() const {
      return 0;
   }
   base() {
      std::cout << value() << std::endl;
   }
   virtual ~base() {}
};

struct derived : public base {
   virtual const int value() const {
      return 1;
   }
};

int main(void) {
   derived example;
}
4

7 回答 7

95

因为base是先构建的,还没有“成熟”成一个derived。当它不能保证对象已经正确初始化时,它不能调用对象上的方法。

于 2009-01-30T17:41:02.937 回答
20

在构造派生对象时,在调用派生类构造函数的主体之前,必须完成基类构造函数。在调用派生类构造函数之前,正在构造的对象的动态类型是基类实例而不是派生类实例。因此,当您从构造函数调用虚函数时,只能调用基类虚函数覆盖。

于 2009-01-30T17:43:23.057 回答
9

实际上,有一种方法可以实现这种行为。“软件中的每个问题都可以通过一定程度的间接来解决。”

/* Disclaimer: I haven't done C++ in many months now, there might be a few syntax errors here and there. */
class parent
{
public:
     parent( ) { /* nothing interesting here. */ };
protected:
     struct parent_virtual
     {
         virtual void do_something( ) { cout << "in parent."; }
     };

     parent( const parent_virtual& obj )
     {
          obj.do_something( );
     }
};

class child : public parent
{
protected:
     struct child_virtual : public parent_virtual
     {
         void do_something( ) { cout << "in child."; }
     };
public:
      child( ) : parent( child_virtual( ) ) { }
};
于 2009-01-31T08:07:10.630 回答
4

它是如何工作的问题是一个常见问题解答项目

总而言之,在T构造类时,动态类型是T,它可以防止对派生类函数实现的虚拟调用,如果允许,可以在相关类不变量建立之前执行代码(Java 和 C# 中的常见问题,但 C++ 是安全的在这方面)。

如何在基类构造函数中进行派生类特定初始化的问题也是一个常见问题解答项目,直接在前面提到的问题之后。

总而言之,使用静态或动态多态可以将相关的函数实现传递给基类构造函数(或类)。

One particular way to do that is to pass a “parts factory” object up, where this argument can be defaulted. For example, a general Button class might pass a button creation API function up to its Widget base class constructor, so that that constructor can create the correct API level object.

于 2014-01-26T21:14:39.247 回答
3

您不应该从构造函数中多态地调用虚方法。相反,您可以在构造对象后调用它们。

您的代码可以重写如下

struct base {
   virtual const int value() const {
      return 0;
   }
   base() {
      /* std::cout << value() << std::endl; */
   }
   virtual ~base() {}
};

struct derived : public base {
   virtual const int value() const {
      return 1;
   }
};

int main(void) {
   derived example;
   std::cout << example.value() << std::endl;
}
于 2009-01-31T01:15:34.750 回答
0

一般规则是您不要从构造函数中调用虚函数。

于 2009-01-30T17:40:01.487 回答
-4

在 C++ 中,您不能从构造函数调用虚拟/覆盖方法。

现在,您可以这样做是有充分理由的。作为“软件中的最佳实践”,您应该尽可能避免从构造函数调用其他方法,甚至是非虚拟方法。

但是,该规则总是有一个例外,因此您可能希望使用“伪构造函数方法”来模拟它们:

#include <iostream>

class base {
   // <constructor>
   base() {
      // do nothing in purpouse
   }
   // </constructor>

   // <destructor>
   ~base() {
      // do nothing in purpouse
   }
   // </destructor>

   // <fake-constructor>
   public virtual void create() {
      // move code from static constructor to fake constructor
      std::cout << value() << std::endl;
   }
   // </fake-constructor>

   // <fake-destructor>
   public virtual void destroy() {
      // move code from static destructor to fake destructor
      // ...
   }
   // </fake-destructor>

   public virtual const int value() const {
      return 0;
   }

   public virtual void DoSomething() {
      // std:cout << "Hello World";
   }
};

class derived : public base {
   // <fake-constructor>
   public override void create() {
      // move code from static constructor to fake constructor
      std::cout << "Im pretending to be a virtual constructor," << std::endl;
      std::cout << "and can call virtual methods" << std::endl;
   }
   // </fake-constructor>


   // <fake-destructor>
   public override void destroy() {
      // move code from static destructor to fake destructor
      std::cout << "Im pretending to be a virtual destructor," << std::endl;
      std::cout << "and can call virtual methods" << std::endl;
   }
   // </fake-destructor>

   public virtual const int value() const {
      return 1;
   }
};

int main(void) {
   // call fake virtual constructor in same line, after real constructor
   derived* example = new example(); example->create();

   // do several stuff with your objects
   example->doSomething();

   // call fake virtual destructor in same line, before real destructor
   example->destroy(); delete example();
}

另外,我建议程序员仅将“struct”用于字段结构,将“class”用于具有字段、方法、构造函数等的结构...

于 2011-03-28T17:10:17.647 回答