使用在堆栈上创建的对象调用虚函数

Calling a virtual function using an object created on stack

我对虚函数的经验和知识不多,所以这个问题可能有一个明显的答案。我只是想了解其中的原因。

我有一个简单的 CTest class,它有两个函数 - func()virtualFunc()。我纯粹出于测试目的在堆栈上创建一个对象,并想观察它的行为。我确实意识到我们应该在堆上使用 new 关键字创建一个对象,但我有兴趣了解对象在堆栈上时虚函数的行为。

使用我目前的代码,当我尝试调用虚函数时会导致段错误。谁能告诉我为什么虚函数会导致段错误而非虚函数不会?

class CTest {
    public:
        void func() {
            printf("function was called.\n");
        }   
        virtual void virtualFunc() {
            printf("virtual function was called.\n");
        }   
};

int main (int argc, char ** argv) {
    CTest * obj = NULL;
    
    obj->func(); // this would print "function called" as what's inside the func()
    
    obj->virtualFunc(); 
    //line above results in segfault because C *obj is on stack; 
    //You would need to allocated obj on the heap for the virtual function to not Seg Fault.WHY?
    //by using "C*obj = new C", I am able to print the contents of virtualFunc() successfully.

}

您需要通过调用 obj = new CTest 来创建对象,而不是调用 obj = NULL

CTest * obj = NULL;

这不是在堆栈上创建对象的方式。这就是您在堆栈上创建对象指针并将其指向任何地方的方式。

你可以简单地做:

CTest obj; // This creates the object, calls constructor

并且由于对象被视为引用,而不是指针,您可以使用

obj.func();
obj.virtualFunc();

等等

要使用指向堆栈变量的指针,您可以这样做:

CTest obj_value; // This actually creates the object
CTest * obj = &obj_value; // This creates pointer to stack object

对象是在栈上还是堆上不影响虚函数的行为。

在你的情况下,obj->func(); 有效并且没有段错误的事实实际上是一个不幸的事实。方法不存储在对象内部。它们存储在可执行文件的代码部分,每个整个程序一次,而不是实例,就像常规函数一样。它们也被翻译成类似:

CTest::func(CTest * this) // here this is obj

并且由于 this 无效,但未被使用,所以没有明显的错误发生。

在虚函数的情况下,每个虚函数调用实际上读取 this 以获取所谓的 vtable。这是您的程序崩溃的地方,因为 this 是空指针。