在 Effective C ++ 一书中,我看到了以下段落:
结果,如果你写
class Empty{};
它基本上和你写的一样:
class Empty { public: Empty() { ... } Empty(const Empty& rhs) { ... } ~Empty() { ... } Empty& operator=(const Empty& rhs) { ... } // copy assignment operator };
以下代码将导致生成每个函数:
Empty e1; Empty e2(e1); e2 = e1;
但是在反汇编通过编译上面的代码创建的可执行文件后,我意识到并非如此:没有任何函数被调用。
这是主要的汇编代码:
00000000004006cd <main>:
4006cd: 55 push %rbp
4006ce: 48 89 e5 mov %rsp,%rbp
4006d1: b8 00 00 00 00 mov $0x0,%eax
4006d6: 5d pop %rbp
4006d7: c3 retq
没有任何名为&#34;空的&#34;在.text
细分中。
那么在我们调用构造函数或赋值空类之后,编译器的行为究竟是什么?这本书说它会产生一些功能吗?如果是的话,它们存放在哪里?
答案 0 :(得分:54)
这些功能存在,但可以内联。
当编译器内联函数时,它意识到它们是无操作,并且没有生成代码。
本书的内容在某种程度上是正确的,编辑器为内联和直接调用创建了名义函数。
但是生成的代码是空的,因此优化编译器将删除该函数的任何证据(设置this指针),并且永远不会直接调用这些函数。
这本书并不是真的试图解释生成的代码,而是创建一个类的影响,以及&#34;隐藏&#34;它为正常操作生成的函数。
答案 1 :(得分:21)
这些方法确实为类生成,但它们生成为&#34; inline&#34;。
因为当class
为空时它们是逐个成员的实现(例如复制构造函数将复制构造所有成员),所以实际上没有在它们中完成任何操作,并且内联它们是&#39;只是看不见。
记住这些方法自动获得实现非常重要...例如代码
struct Foo {
char *buf;
Foo() : buf(new char[10]) {}
~Foo() { delete[] buf; }
};
是错误的,因为复制构造函数和赋值的自动生成代码是错误的,并且会导致多次删除缓冲区。
这不是因为已经写过的东西,而是因为写了不的东西,这很棘手。这就是为什么要记住C ++会自动为你编写的内容非常重要:如果那个实现是你想要的那么完美,但如果没有那么通过提供正确的实现来修复它或者禁止创建或使用那个错误的代码
答案 2 :(得分:9)
你和这本书正是从不同的抽象层面来看待这种情况。
本书使用术语“generated”来指代由编译器隐式定义到抽象C ++程序中的C ++函数。这绝对会发生。
您将其解释为翻译程序中实际生成的实际机器代码。这不是它的意思。只要保留原始抽象程序的语义,实际机器代码的生成始终受编译器的异想天开。
因此,这本书当然不是不正确的,尽管为了清晰起见,我可能会使用不同的词。坚持标准术语永远不会伤害。