1

更新:此问题是由于内存使用不当引起的,请参阅底部的解决方案。

这是一些半伪代码:

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调用 from 时不调用函数ClassA::SomeFunction?而是ClassA::VirtualFunction被称为...

当我强制实现虚函数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;
}

尽管已明确声明和定义了派生函数,但在运行时仍会发生以下错误。

pure virtual method called
terminate called without an active exception

注意:似乎错误可能是由于内存使用不当引起的。有关详细信息,请参阅自我回答。

更新 1 - 4:

已删除评论(不相关)。

解决方案:

发布为答案。

4

8 回答 8

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();
}

作为旁注,请注意不要从构造函数或析构函数调用虚函数,因为您可能会得到意想不到的结果。

于 2009-04-10T16:49:24.340 回答
4

如果您收到“纯虚方法调用终止调用没有活动异常”错误消息,这意味着您正在从 classA(基类)的构造函数或析构函数调用虚函数,您不应该这样做。

于 2009-04-10T17:57:35.050 回答
3

关于称为错误的纯虚方法:

您应该创建一个不同的问题,因为它实际上与另一个不同。这个问题的答案在我之前对您最初问题的回答的最后一段:

不要从构造函数或析构函数调用虚函数

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。编译器将为类型 Base 生成虚方法表,其中f()的条目为0(未在 base 中实现)。然后编译器将执行构造函数中的代码。在基础部分完全构建后,派生元素部分的构建开始。编译器将更改虚拟表,以便f()的条目指向Derived::f()

如果您尝试在仍在构造 Base 的同时调用方法f(),则虚拟方法表中的条目仍然为空,并且应用程序崩溃。

于 2009-04-10T18:15:49.110 回答
1

当 A 调用 VirtualFunction() 时,它会自动调用 B 上的版本。这就是虚函数的意义所在。

我对 C++ 语法不太熟悉。您是否必须在正文和标头中将函数声明为虚拟函数?

另外,在 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
    }
}
于 2009-04-10T16:47:13.330 回答
0

您没有正确定义 ClassB 中的函数,它应该是:

public class ClassB
{
    public void override AbstractFunction()
    {
        // hello world
    }
}

然后,从基类到虚拟/抽象方法的任何调用都将调用派生实例上的实现。

于 2009-04-10T16:51:06.957 回答
0

如果要强制派生类实现VirtualFunction

class ClassA
{
public:
    virtual void VirtualFunction()=0;
    void SomeFunction();
}

这是 C++。默认将调用派生函数。

如果要调用基类函数,请执行以下操作:

void ClassA::SomeFunction()
{
    // ... various lines of code ...

     ClassA::VirtualFunction();
}
于 2009-04-10T16:54:03.643 回答
0

您的代码没有问题,但您的示例不完整。您没有说明您从哪里调用 SomeFunction 。

正如 dribeas 已经指出的那样,您必须小心地从构造函数中调用虚函数,因为只有在层次结构中的每个类完成构造时才会构建虚表。

编辑:我的回复的以下段落不正确。道歉。可以从 ClassB 的构造函数调用 SomeFunction,因为 vtable 在初始化程序列表的末尾(至少)就位,即一旦您在构造函数的主体中。当然不能从 ClassA 的构造函数中调用它。

原文段落:

我怀疑您必须从 ClassB 的构造函数调用 SomeFunction,此时只有达到 ClassA 类型的 vtable 才能完成,即您的类仍然是 ClassA 类型的虚拟调度机制。只有在构造函数完成时,它才会成为 ClassB 类型的对象。

于 2009-04-10T17:41:59.550 回答
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;
....

但有时有一个空的定义是合法的。这将取决于您的确切用法。

于 2009-04-10T20:05:04.230 回答