C++在一个空class中写调用什么函数?
What function does C++ write and call in an empty class?
在Effective C++一书中,我看到了下面这段话:
As a result, if you write
class Empty{};
it's essentially the same as if you'd written this:
class Empty {
public:
Empty() { ... }
Empty(const Empty& rhs) { ... }
~Empty() { ... }
Empty& operator=(const Empty& rhs) { ... } // copy assignment operator
};
The following code will cause each function to be generated:
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 [=14=]x0,%eax
4006d6: 5d pop %rbp
4006d7: c3 retq
.text
段中没有任何名为 "Empty" 的函数。
那么在我们调用构造函数或空赋值 class 之后,编译器的行为究竟是什么?是不是像书上说的那样产生了一些功能?如果是,它们存储在哪里?
函数存在,但可以内联。
当编译器内联函数时,它意识到它们是空操作,并且没有生成代码。
这本书说的在一定程度上是正确的,概念函数是由编译器创建的,用于内联和直接调用。
但生成的代码是空的,因此优化编译器会删除函数的任何证据(设置 this 指针),函数将永远不会被直接调用。
本书并没有真正试图解释生成的代码,而是解释创建 class 的影响,以及它为正常运行生成的 "hidden" 函数。
这些方法确实是为 class 生成的,但它们是为 "inline" 生成的。
因为它们是逐个成员的实现(例如,复制构造函数将复制构造所有成员)当 class
为空时,它们实际上什么也没做,并且内联它们是只是看不见。
重要的是要记住这些方法会自动获得一个实现...例如代码
struct Foo {
char *buf;
Foo() : buf(new char[10]) {}
~Foo() { delete[] buf; }
};
有bug,因为自动生成的拷贝构造函数和赋值代码是错误的,会导致多次删除缓冲区。
它有问题不是因为写了什么东西,而是因为 没有 写的东西,这很棘手。这就是为什么记住 C++ 会自动为您编写的内容极其重要的原因:如果该实现是您想要的,那么完美,但如果不是,则通过提供正确的实现或禁止创建或使用错误的代码来修复它。
你和这本书是从不同的抽象层次来看待这种情况的。
本书使用术语 "generated" 来指代编译器隐式定义到抽象 C++ 程序中的 C++ 函数。这绝对会发生。
您将其解释为在翻译程序中实际生成实际机器代码。这不是它的意思。只要保持原始抽象程序的语义,实际机器代码的生成总是受制于编译器的突发奇想。
因此,这本书当然没有错,尽管为了清楚起见,我可能会使用不同的词。坚持使用标准术语永远不会有坏处。
在Effective C++一书中,我看到了下面这段话:
As a result, if you write
class Empty{};
it's essentially the same as if you'd written this:
class Empty { public: Empty() { ... } Empty(const Empty& rhs) { ... } ~Empty() { ... } Empty& operator=(const Empty& rhs) { ... } // copy assignment operator };
The following code will cause each function to be generated:
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 [=14=]x0,%eax
4006d6: 5d pop %rbp
4006d7: c3 retq
.text
段中没有任何名为 "Empty" 的函数。
那么在我们调用构造函数或空赋值 class 之后,编译器的行为究竟是什么?是不是像书上说的那样产生了一些功能?如果是,它们存储在哪里?
函数存在,但可以内联。
当编译器内联函数时,它意识到它们是空操作,并且没有生成代码。
这本书说的在一定程度上是正确的,概念函数是由编译器创建的,用于内联和直接调用。
但生成的代码是空的,因此优化编译器会删除函数的任何证据(设置 this 指针),函数将永远不会被直接调用。
本书并没有真正试图解释生成的代码,而是解释创建 class 的影响,以及它为正常运行生成的 "hidden" 函数。
这些方法确实是为 class 生成的,但它们是为 "inline" 生成的。
因为它们是逐个成员的实现(例如,复制构造函数将复制构造所有成员)当 class
为空时,它们实际上什么也没做,并且内联它们是只是看不见。
重要的是要记住这些方法会自动获得一个实现...例如代码
struct Foo {
char *buf;
Foo() : buf(new char[10]) {}
~Foo() { delete[] buf; }
};
有bug,因为自动生成的拷贝构造函数和赋值代码是错误的,会导致多次删除缓冲区。
它有问题不是因为写了什么东西,而是因为 没有 写的东西,这很棘手。这就是为什么记住 C++ 会自动为您编写的内容极其重要的原因:如果该实现是您想要的,那么完美,但如果不是,则通过提供正确的实现或禁止创建或使用错误的代码来修复它。
你和这本书是从不同的抽象层次来看待这种情况的。
本书使用术语 "generated" 来指代编译器隐式定义到抽象 C++ 程序中的 C++ 函数。这绝对会发生。
您将其解释为在翻译程序中实际生成实际机器代码。这不是它的意思。只要保持原始抽象程序的语义,实际机器代码的生成总是受制于编译器的突发奇想。
因此,这本书当然没有错,尽管为了清楚起见,我可能会使用不同的词。坚持使用标准术语永远不会有坏处。