在实施之前调用虚函数

时间:2011-10-26 15:29:04

标签: c++ virtual

所以我有这种设置

class Base
{
public:
  Base();
  virtual void parse() = 0;
};

Base::Base()
{
  parse();
} 

class Sub : public Base
{
public:
  Sub();
  void parse();
};

Sub::Sub() : Base() {}

void Sub::parse()
{
  // DO stuff
}

我想知道是否还有我可以做类似的事情,现在我收到一个错误,说我无法调用一个有意义的纯虚函数。我有没有可以使用的关键字来完成这项工作?

我认为使解析()只是虚拟而不是纯虚拟会起作用,但我希望用户必须覆盖它。

4 个答案:

答案 0 :(得分:10)

在构造函数(或析构函数)中调用虚拟成员函数 永远不会让您在派生类中以该函数的重写版本 结束。

原因是基类构造函数(析构函数)在派生类'构造函数(析构函数)之前(之后)执行。这意味着表示派生类的对象部分只是 尚未 (不再是) 现有 。并且无法在不存在的对象上调用成员函数。

您需要实施一种 两阶段构建 (不包含在语言中),以便做您想做的事。通常,通过让 包装 类完全构造一个Sub对象,然后再调用parse()来完成。

答案 1 :(得分:2)

这里的要点是你不能在构造函数中调用纯虚函数,即使你提供了一个实现,也只能在构造函数中使用Base类的实现。

原因很简单,Base的构造函数在Sub的开头执行,因此对Sub的任何虚函数调用都将在不完整的对象上调用。

通常,没有解决方案:您不能在构造函数或析构函数中调度虚函数。

答案 2 :(得分:2)

虚函数的分辨率无法在类中找到函数 还没有建成。 (形式上:动态类型的 object是正在运行的构造函数或析构函数的对象。)所以 你需要某种后构造函数,而语言却没有 支持。

您可以使用调用的伪参数来解决它 析构函数中需要的函数,例如:

class Base
{
public:
    class PostConstructor
    {
        Base* owner;
        friend class Base;
    public:
        PostConstructor() : owner( NULL ) {}
        ~PostConstructor() { owner->parse(); }
    };
    Base( PostConstructor const& helper )
    {
        helper.owner = this;
    }
};

class Derived : public Base
{
public:
    Derived( PostConstructor const& helper = PostConstructor() )
        : Base( helper )
    {
    }
};

或者如果类有参数:

class Base
{
    std::string name;
public:
    class PostConstructor
    {
        Base* owner;
        std::string arg;    //  for example...
        friend class Base;
    public:
        PostConstructor( std::string const& arg )  // implicit conversion!!
                : owner( NULL ), arg( arg ) {}
        ~PostConstructor() { owner->parse(); }
        operator std::string() const { return arg; }
    };
    Base( PostConstructor const& helper )
        : name( helper )
    {
        helper.owner = this;
    }
};

class Derived : public Base
{
public:
    Derived( PostConstructor const& helper )
        : Base( helper )
    {
    }
};

这是有效的,因为PostConstructor将是一个临时的,被破坏的 在完整表达结束时(Derived完全表达时) 构造)。

在这两种情况下:派生类必须合作(但他们不会合作) 编译,如果他们不)。如果是这样的话,诀窍也会失败 构造在一个更复杂的表达中间。某物 像:

Derived(...).someFunction();

在这种情况下,完整表达式的结尾将在返回之后 来自someFunction,可能有点迟到来致电parse()

尽管存在局限性,但我发现这种情况很有用。

答案 3 :(得分:0)

抽象或纯方法是虚方法的一个特例(所有抽象或纯方法都是虚方法)。

我之前的回答是错误的,因为我忽略了构造函数。 C ++上的构造函数不是虚拟的,不允许在构造函数中调用虚拟(抽象方法而不是抽象方法)。如果你从另一个不是构造函数的方法调用非抽象覆盖“解析”,那就没问题了。

问题不在于它的方法是抽象的,而是从构造函数调用它。

#include <conio>

class Base
{
public:
  // a constructor:
  Base();

  // "virtual and abstract" method:
  virtual void parse() = 0;

  // "virtual non abstract" method:
  virtual void hello();
};

// Error: you cannot call a virtual method from a constructor,
// wheter is abstract or not:
Base::Base()
{
  // error:
  parse();

  // error:
  hello();
} 

Base::hello()
{
  cout << "Hello World\n";
} 

class Sub : public Base
{
public:
  Sub();

  // forgot "virtual" here,
  // other languages use "override" instead, here:
  virtual void parse();
  // another "overriden" methods:
  virtual void parse();
};

// right: its important to call the base constructor,
// in this case:
Sub::Sub() : Base()
{
  // ...
}

void Sub::parse()
{
  // DO stuff
}

int main()
{
  Base *MyBaseObject = new Base();
  MyObject->parse();    

  Sub *MyObject = new Sub();
  MyObject->parse();

  return 0;
}

有一种解决方法。要调用虚方法, 就像它是从构造函数调用,声明一个新方法,它在构造函数之后调用:

#include <conio>

class Base
{
public:
  // a constructor:
  Base();

  // a "postconstructor" or "pseudoconstructor"
  virtual void create();

  // "virtual and abstract" method:
  virtual void parse() = 0;

  // "virtual non abstract" method:
  virtual void hello();
};

// Error: you cannot call a virtual method from a constructor,
// wheter is abstract or not:
Base::Base()
{
  // no virtual methods called here,
  // wheter abstract or not
} 

// its not a real constructor, just a virtual method:
void Sub::create()
{
  // ...
}   
Base::hello()
{
  cout << "Hello World\n";
} 

class Sub : public Base
{
public:
  Sub();

  virtual void create();

  // forgot "virtual" here,
  // other languages use "override" instead, here:
  virtual void parse();
  // another "overriden" methods:
  virtual void parse();
};

// right: its important to call the base constructor,
// in this case:
Sub::Sub() : Base()
{
  // ...
}

// its not a real constructor, just a virtual method:
void Sub::create() : create()
{
  parse();
}

void Sub::parse()
{
  // DO stuff
}

int main()
{
  // this commented code, wont work
  /*
  Base *MyBaseObject = new Base();
  MyObject->create();    
  MyObject->parse();    
  */

  // calling "pseudo-constructor",
  // just after real constructor
  Sub *MyObject = new Sub(); MyObject->create();
    MyObject->parse();

  return 0;
}

我的错误抱歉。