程序是如何通过链接器的?

时间:2014-05-10 11:26:36

标签: c++

这是程序link_function.cpp

#include <iostream>

class Base {
    public:
        Base() {
            init();     // first condition
         //   log();    // second condition
        }

        virtual ~Base() {}

        virtual void log() = 0;
    private:
        void init() {
            log();
        }
};

class Derived: public Base {
    virtual void log() {}
};

int main() {
    Derived d;
    return 0;
}

第一个条件

make link_function过去了。 ./link_function产生了如下错误。

pure virtual method called
terminate called without an active exception
Aborted (core dumped)

init()函数是如何通过链接器而不产生undefined to reference log()错误的?

第二个条件

当我评论int()并且只是调用log()时,它产生了linker error

undefined reference to 'Base::log()'

他们之间的差异是什么?

2 个答案:

答案 0 :(得分:2)

派生类是从基类构建的。在Base的构造函数期间,Derived的构造函数尚未执行。因此,vtable尚未完成。

在构造函数中调用虚函数(甚至是非纯函数)是顽皮的。这意味着不正确的课程设计。

这就是为什么这是一个坏主意:

#include <iostream>

using namespace std;

class Base {
    public:
        Base() {
            init();
         //   log();
        }

        virtual void log() { cout << "stange outcome 1" << endl; }
    private:
        void init() {
            log();
        }
};

class Derived: public Base {
    public:
    virtual void log() { cout << "stange outcome 2" << endl;}

    Derived() 
    : Base() 
    { 
        log();
    }
};

int main() {
    Derived d;
    d.log();
    return 0;
}

输出:

Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1

Executing the program....
$demo 
stange outcome 1
stange outcome 2
stange outcome 2

解释原因:

当我们创建Derived类的对象时,程序执行以下操作:

  1. 为Derived
  2. 分配足够大的内存
  3. 将对象的vtable指针设置为指向Base的vtable
  4. 执行Base :: Base()[调用log()将调用Base :: log()]
  5. 将对象的vtable指针设置为指向Dervied的vtable
  6. 执行Derived :: Derived()[调用log()将调用Dervied :: log()]
  7. 对象现已完全构建。

答案 1 :(得分:0)

您的log功能是虚拟的,因此链接器并不担心。 Linker并不关心虚函数,因为它们的地址仅在运行时才知道。

您的代码的问题是您从Base构造函数调用虚函数。但是在调用Base构造函数时,Derived尚未构造,因此log函数尚未定义。

Derived类的每个对象包含&#34;子对象&#34;在对象本身之前构造的Base类(即Base构造函数在Derived之前调用)。当Base构造函数执行时,它不知道Derived类的虚拟表,因此它无法解析log要调用的地址。因此,它认为它是纯虚函数并中止执行。