2016-08-23 36 views
43

在书中有效的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段命名为“空”的任何功能。

那么在我们调用构造函数或空类的赋值后,编译器的行为究竟是什么?这本书是否会产生一些功能?如果是这样,他们在哪里存储?

+12

您是否使用优化进行编译? – Rakete1111

+8

为了迂回,'Empty(){...}'与编译器生成的*不完全相同,要得到与编译器生成的相同的结果,您需要'Empty()= default;'。有微妙的差异 - 其他成员也是如此。看到这里:http://en.cppreference.com/w/cpp/language/default_constructor#Trivial_default_constructor –

+0

没有任何优化 – linvoker

回答

54

函数存在,但可以内联。

当编译器内联函数时,它意识到它们是空操作,并且没有生成代码。

本书的内容在某种程度上是真实的,编译器创建了名义函数,用于内联和直接调用。

但是生成的代码是空的,所以优化编译器会删除该函数的任何证据(设置这个指针),并且函数将永远不会被直接调用。

这本书没有真正解释生成的代码,而是创建类的影响,以及它为正常操作生成的“隐藏”功能。

+17

另外你可能想阅读关于[as-if rule](http://stackoverflow.com/questions/15718262/what-exactly-is-the-as-if-rule) – MatthewRock

21

这些方法确实是为类生成的,但它们是以“内联”的方式生成的。

class为空时,由于它们是逐个成员的实现(例如复制构造函数将复制构建所有成员),所以实际上没有任何事情在其中进行,并且内联它们只是不可见。

但是要记住,这些方法得到自动的实现是非常重要的......例如代码

struct Foo { 
    char *buf; 
    Foo() : buf(new char[10]) {} 
    ~Foo() { delete[] buf; } 
}; 

是越野车,因为拷贝构造函数和赋值自动生成的代码是错误的,并会导致多次删除缓冲区。

这是错误的不是因为已经写好的东西,而是因为已经编写了而不是,这很棘手。这就是为什么记住C++会自动为你写什么是非常重要的:如果这个实现是你想要的然后完美的,但是如果没有,那么通过提供正确的实现来修复它,或者禁止创建或使用那个错误的代码。

9

你和本书是从不同抽象层次的这种情况出现的。

本书使用术语“生成”来指代由编译器隐式定义到抽象C++程序中的C++函数。这绝对会发生。

您将其解释为翻译程序中的实际机器代码的实际生成。这不是这个意思。只要保持原始抽象程序的语义,实际机器代码的生成总是受制于编译器的奇思妙想。

因此,这本书当然不是不正确的,但为了清晰起见,我可能会使用不同的词。坚持标准术语从不会伤害。

+2

这是我最喜欢的答案,因为它更多一般。内嵌函数与否,编译器可以不输出,因为程序什么都不做。抽象C++程序和具体的机器代码实现之间的区别是非常重要的。 –

+0

@JordanMelo:很高兴你喜欢它 - 这正是我要去的! –