什么是nm告诉我关于在Linux上使用gcc编译的程序中的vtable?

Wil*_*ill 2 c++ inheritance gcc vtable nm

我将Base类更改为抽象(即我将其中一个方法设为纯虚拟)并重新编译它.当我将它与派生类链接时,链接器抱怨了vtable.我用nm调查了一些事情,但我不确定nm告诉我的是什么.我只是通过删除*.o文件并重新编译Derived类来解决问题,但我想了解这里的vtable究竟发生了什么.

我的原始代码是这样的:

Base.h

class Base {
    public:
       virtual void doSomething();
};
Run Code Online (Sandbox Code Playgroud)

Base.cpp

#include <iostream>
#include <Base.h>
void Base::doSomething() {
    std::cout << "Base::doSomething()" << "\n";
}
Run Code Online (Sandbox Code Playgroud)

Derived.h

#include <Base.h>
class Derived : public Base {
    public:
        Derived();
        void doSomething() override;
};
Run Code Online (Sandbox Code Playgroud)

Derived.cpp

#include <iostream>
#include <Derived.h>
Derived::Derived() {
    std::cout << "Derived::Derived() constructor" << "\n";
}
void Derived::doSomething() {
    std::cout << "Derived::doSomething()" << "\n";
}
Run Code Online (Sandbox Code Playgroud)

Makefile包含以下内容:

CXXFLAGS = -std=c++14 -pedantic -Wall -Werror -I ./

default: build
clean:
    rm -f proggy
    rm -f *.o

proggy: proggy.o Base.o Derived.o 
    g++ -o proggy proggy.o Base.o Derived.o

Base.o: Base.cpp Base.h
Derived.o: Derived.cpp Derived.h
Run Code Online (Sandbox Code Playgroud)

然后我跑了make,一切都很顺利.为了记录,我在这一点上也运行了nm,如下所示:

$ nm -C Derived.o | grep Base
00000000 W Base::Base()
00000000 W Base::Base()
00000000 n Base::Base()
         U typeinfo for Base
         U vtable for Base
Run Code Online (Sandbox Code Playgroud)

我在哪里看到Base的typeinfo是未定义的,但是这一切看起来都很开心.

另外,在Base.o中有这个阶段:

nm -C Base.o | grep Base
000000d4 t _GLOBAL__sub_I__ZN4Base11doSomethingEv
00000000 T Base::doSomething()
00000044 R typeinfo for Base
0000004c R typeinfo name for Base
00000038 R vtable for Base
Run Code Online (Sandbox Code Playgroud)

然后,我按如下方式更改了Base.h和Base.cpp,以使类抽象:

Base.h

class Base {
    public:
       virtual void doSomething() = 0; // pure virtual
};
Run Code Online (Sandbox Code Playgroud)

Base.cpp

#include <iostream>
#include <Base.h>
// void Base::doSomething() {
//     std::cout << "Base::doSomething()" << "\n";
// }
Run Code Online (Sandbox Code Playgroud)

然后当我运行make时出现此错误:

$ make proggy
g++ -std=c++14 -pedantic -Wall -Werror -I ./   -c -o Base.o Base.cpp
g++ -o proggy proggy.o Base.o Derived.o
Derived.o:(.rodata+0x5c): undefined reference to `typeinfo for Base'
Derived.o: In function `Base::Base()':
Derived.cpp:(.text._ZN4BaseC2Ev[_ZN4BaseC5Ev]+0x48): undefined reference to `vtable for Base'
collect2: error: ld returned 1 exit status
Makefile:17: recipe for target 'proggy' failed
make: *** [proggy] Error 1
Run Code Online (Sandbox Code Playgroud)

其中c ++ filt告诉我以下内容:

$ c++filt _ZN4BaseC2Ev
Base::Base()
Run Code Online (Sandbox Code Playgroud)

所以我接着运行nm如下,它告诉我这个:

$ nm -C Derived.o | grep Base
00000000 W Base::Base()
00000000 W Base::Base()
00000000 n Base::Base()
         U typeinfo for Base
         U vtable for Base
Run Code Online (Sandbox Code Playgroud)

我可以看到Base的typeinfo是未定义的,但它也是一开始的,所以我认为这不是一个问题,但确实如此.另请注意,现在没有在Base.o中提及Base.

$ nm -C Base.o | grep Base  
Run Code Online (Sandbox Code Playgroud)

即这个nm和grep命令没有找到任何东西.

最后,我删除了所有*.o文件并再次运行make,一切都很好.然后我运行nm来查看nm报告,并报告以下内容:

$ nm -C Derived.o | grep Base
00000000 W Base::Base()
00000000 W Base::Base()
00000000 n Base::Base()
00000000 V typeinfo for Base
00000000 V typeinfo name for Base
00000000 V vtable for Base
Run Code Online (Sandbox Code Playgroud)

这是什么意思呢?

我的问题是:

  1. 什么是nm在这里告诉我以及之前vtable出了什么问题?
  2. 什么导致需要它没有?
  3. 为什么重新编译派生类修复了什么以及这对vtable做了什么修复?

Min*_*ine 6

从日志

$ make proggy
g++ -std=c++14 -pedantic -Wall -Werror -I ./   -c -o Base.o Base.cpp
g++ -o proggy proggy.o Base.o Derived.o
Run Code Online (Sandbox Code Playgroud)

这意味着你的makefile不会重新构建Derived.o,只能重新构建Base.o.这当然会引起这个问题.

你需要修复的Makefile添加适当的依赖于Derive.cppBase.h.

  • 相关的语言 - 律师解释是因为两个翻译单元编译了不同的"基础"定义,我们得到了未定义的行为. (2认同)