所以我有这种设置
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
}
我想知道是否还有我可以做类似的事情,现在我收到一个错误,说我无法调用一个有意义的纯虚函数。我有没有可以使用的关键字来完成这项工作?
我认为使解析()只是虚拟而不是纯虚拟会起作用,但我希望用户必须覆盖它。
答案 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;
}
我的错误抱歉。