C++在空类中编写和调用什么函数?
在书中有效的C++,我看见了下面一段话: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
没有在.text
段命名为“空”的任何功能。
那么在我们调用构造函数或空类的赋值后,编译器的行为究竟是什么?这本书是否会产生一些功能?如果是这样,他们在哪里存储?
函数存在,但可以内联。
当编译器内联函数时,它意识到它们是空操作,并且没有生成代码。
本书的内容在某种程度上是真实的,编译器创建了名义函数,用于内联和直接调用。
但是生成的代码是空的,所以优化编译器会删除该函数的任何证据(设置这个指针),并且函数将永远不会被直接调用。
这本书没有真正解释生成的代码,而是创建类的影响,以及它为正常操作生成的“隐藏”功能。
另外你可能想阅读关于[as-if rule](http://stackoverflow.com/questions/15718262/what-exactly-is-the-as-if-rule) – MatthewRock
这些方法确实是为类生成的,但它们是以“内联”的方式生成的。
当class
为空时,由于它们是逐个成员的实现(例如复制构造函数将复制构建所有成员),所以实际上没有任何事情在其中进行,并且内联它们只是不可见。
但是要记住,这些方法得到自动的实现是非常重要的......例如代码
struct Foo {
char *buf;
Foo() : buf(new char[10]) {}
~Foo() { delete[] buf; }
};
是越野车,因为拷贝构造函数和赋值自动生成的代码是错误的,并会导致多次删除缓冲区。
这是错误的不是因为已经写好的东西,而是因为已经编写了而不是,这很棘手。这就是为什么记住C++会自动为你写什么是非常重要的:如果这个实现是你想要的然后完美的,但是如果没有,那么通过提供正确的实现来修复它,或者禁止创建或使用那个错误的代码。
你和本书是从不同抽象层次的这种情况出现的。
本书使用术语“生成”来指代由编译器隐式定义到抽象C++程序中的C++函数。这绝对会发生。
您将其解释为翻译程序中的实际机器代码的实际生成。这不是这个意思。只要保持原始抽象程序的语义,实际机器代码的生成总是受制于编译器的奇思妙想。
因此,这本书当然不是不正确的,但为了清晰起见,我可能会使用不同的词。坚持标准术语从不会伤害。
这是我最喜欢的答案,因为它更多一般。内嵌函数与否,编译器可以不输出,因为程序什么都不做。抽象C++程序和具体的机器代码实现之间的区别是非常重要的。 –
@JordanMelo:很高兴你喜欢它 - 这正是我要去的! –
您是否使用优化进行编译? – Rakete1111
为了迂回,'Empty(){...}'与编译器生成的*不完全相同,要得到与编译器生成的相同的结果,您需要'Empty()= default;'。有微妙的差异 - 其他成员也是如此。看到这里:http://en.cppreference.com/w/cpp/language/default_constructor#Trivial_default_constructor –
没有任何优化 – linvoker