更新:此问题是由内存使用率不佳引起的,请参见底部的解决方案。
这是一些半伪代码:
class ClassA
{
public:
virtual void VirtualFunction();
void SomeFunction();
}
class ClassB : public ClassA
{
public:
void VirtualFunction();
}
void ClassA::VirtualFunction()
{
// Intentionally empty (code smell?).
}
void ClassA::SomeFunction()
{
VirtualFunction();
}
void ClassB::VirtualFunction()
{
// I'd like this to be called from ClassA::SomeFunction()
std::cout << "Hello world!" << endl;
}
C#等价物如下:删除了C#示例,因为它与实际问题无关。
从ClassB::VirtualFunction
调用时,为什么不调用ClassA::SomeFunction
函数?相反,ClassA::VirtualFunction
被称为......
当我force implementation of the virtual function ClassA :: VirtualFunction时,像这样:
class ClassA
{
public:
virtual void VirtualFunction() = 0;
void SomeFunction();
}
class ClassB : public ClassA
{
public:
void VirtualFunction();
}
void ClassA::SomeFunction()
{
VirtualFunction();
}
void ClassB::VirtualFunction()
{
// I'd like this to be called from ClassA::SomeFunction()
std::cout << "Hello world!" << endl;
}
在运行时发生以下错误,尽管已经声明并定义了deriinately。
pure virtual method called
terminate called without an active exception
注意:看起来即使内存使用不良也可能导致错误。有关详细信息,请参阅自我答案。
删除了评论(不是重新发布)。
答案 0 :(得分:5)
class Base {
public:
virtual void f() { std::cout << "Base" << std::endl; }
void call() { f(); }
};
class Derived : public Base {
public:
virtual void f() { std::cout << "Derived" << std::endl; }
};
int main()
{
Derived d;
Base& b = d;
b.call(); // prints Derived
}
如果在Base类中你不想实现该函数,则必须声明:
class Base {
public:
virtual void f() = 0; // pure virtual method
void call() { f(); }
};
编译器不允许你实例化类:
int main() {
//Base b; // error b has a pure virtual method
Derived d; // derive provides the implementation: ok
Base & b=d; // ok, the object is Derived, the reference is Base
b.call();
}
作为附注,注意不要从构造函数或析构函数中调用虚函数,因为您可能会得到意想不到的结果。
答案 1 :(得分:4)
如果您正在使用“纯虚拟方法” 在没有活动异常的情况下调用终止'错误消息,这意味着你从classA(基类)的构造函数或析构函数中调用虚函数,这是你不应该做的。
答案 2 :(得分:3)
在名为error的纯虚方法上:
你应该创建一个不同的问题,因为它实际上与另一个不同。这个问题的答案在我之前对你的初步问题的答案的最后一段:
不要从构造函数或析构函数中调用虚函数
class Base
{
public:
Base() { f(); }
virtual void f() = 0;
};
class Derived : public Base
{
public:
virtual void f() {}
};
int main()
{
Derived d; // crashes with pure virtual method called
}
上面代码中的问题是编译器将允许您实例化Derived类型的对象(因为它不是抽象的:所有虚拟方法都已实现)。一个班级的建设从所有基地的建设开始,在这种情况下是基地。编译器将为类型Base生成虚方法表,其中 f()的条目为0(未在base中实现)。然后编译器将在构造函数中执行代码。在完成构建基础部件之后,将开始构建派生元素部件。编译器将更改虚拟表,以便 f()的条目指向 Derived :: f()。
如果在构建Base时尝试调用方法 f(),则虚方法表中的条目仍为null,应用程序崩溃。
答案 3 :(得分:1)
当A调用VirtualFunction()时,它会自动调用B上的版本。这是虚函数的重点。
我对C ++语法不熟悉。您是否必须在主体和标题中声明该函数是虚拟的?
Alsop,在B级你可能需要将其标记为覆盖
在C#中很容易。我只是不知道c ++语法。public class ClassA
{
public **virtual** void VirtualFunction(){}
public void FooBar()
{
// Will call ClassB.VirtualFunction()
VirtualFunction();
}
}
public class ClassB
{
public **overide** void VirtualFunction()
{
// hello world
}
}
答案 4 :(得分:0)
您没有正确定义ClassB中的函数,它应该是:
public class ClassB
{
public void override AbstractFunction()
{
// hello world
}
}
然后,从基类到虚拟/抽象方法的任何调用都将调用派生实例上的实现。
答案 5 :(得分:0)
如果要强制派生类实现VirtualFunction
:
class ClassA
{
public:
virtual void VirtualFunction()=0;
void SomeFunction();
}
这是C ++。默认情况下将调用派生函数。
如果要调用基类函数,请执行以下操作:
void ClassA::SomeFunction()
{
// ... various lines of code ...
ClassA::VirtualFunction();
}
答案 6 :(得分:0)
您的代码没有任何问题,但您的示例不完整。你没有说明你从哪里调用SomeFunction。
正如dribeas已经指出的那样,你必须小心从构造函数中调用虚函数,因为虚拟表只是在层次结构中的每个类完成构造时构建的。
编辑:我的回复的以下段落不正确。道歉。可以从ClassB的构造函数中调用SomeFunction,因为vtable在初始化列表的末尾(至少)就位,即一旦你在构造函数的主体中。当然,从ClassA的构造函数中调用它是不对的。
原始段落:
我怀疑你必须从ClassB的构造函数中调用SomeFunction,此时只有类型为ClassA的vtable才能完成,即对于虚拟调度机制,你的类仍然是ClassA类型。它只在构造函数完成时成为ClassB类型的对象。 的
答案 7 :(得分:0)
要调用虚拟功能函数,您需要通过指针或引用进行调用。
void ClassA::SomeFunction()
{
VirtualFunction(); // Call ClassA::VirtualFunction
this->VirtualFunction(); // Call Via the virtual dispatch mechanism
// So in this case call ClassB::VirtualFunction
}
您需要能够区分两种不同类型的调用,否则当覆盖它时,classA :: VirtualFunction()将变得不可访问。
正如其他人所指出的,如果你想让基类版本抽象,那么使用= 0而不是{}
class A
{
virtual void VirtualFunction() =0;
....
但有时候有一个空的定义是合法的。这取决于您的确切用法。