Contents

StackOverflow上有个一个有趣的问题,copy by value to function params produce two objects in vs2012

给出程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>

using namespace std;

class A
{
public:
A() { cout << "1" << endl; }
~A() { cout << "2" << endl; }
};

class B : public A
{
public:
B() { cout << "3" << endl; }
~B() { cout << "4" << endl; }
};

void func(A a) {}

int main()
{
B b;
func(b);
return 0;
}

Visual Studio给出的输出是:132242。

前两个13和最后两个42.是对应的,分别是main函数中的局部变量b的构造和析构。问题出在中间的两个2,说明在调用函数func的时候创建了两个临时的A。

看到这个程序,第一眼的感觉应该是:因为func是参数是值传递,所以b传给func时,调用了A的拷贝构造函数,从b生成了一个A的临时变量,推出func时,这个临时变量析构,所以应该只有一个2.

 

但是为什么会有两次呢?打开disassembly window,我们可以看到如下的汇编代码:

1
2
3
4
5
6
7
8
9
10
11
B b;
00B317F8 lea ecx,[b]
00B317FB call B::B (0B31650h)
00B31800 mov dword ptr [ebp-4],0
func(b);
00B31807 mov al,byte ptr [ebp-12h]
00B3180A mov byte ptr [ebp-13h],al
00B3180D mov byte ptr [ebp-4],1
00B31811 movzx ecx,byte ptr [ebp-13h]
00B31815 push ecx
00B31816 call func (0B31730h)

如果我们把原来的程序的中A的析构函数改成virtual,这样,输出的结果就是13242,中间只有1个2,说明只有一个临时的A被创建了出来,是我们期望的结果。汇编代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
B b;
lea ecx,[b]
0033189B call B::B (03316A0h)
003318A0 mov dword ptr [ebp-4],0
func(b);
003318A7 push ecx
003318A8 mov ecx,esp
003318AA mov dword ptr [ebp-1Ch],esp
003318AD lea eax,[b]
003318B0 push eax
003318B1 call A::A (0331900h)
003318B6 mov dword ptr [ebp-20h],eax
003318B9 call func (03317D0h)

回想Inside C++ object model中说到的只有在4中情况下编译器才会给我们生成缺省拷贝构造函数:


  1. 类包含的成员变量是object,并且这个object的类有拷贝构造函数。


  2. 类继承自一个基类,这个基类有拷贝构造函数。


  3. 类声明了1个或者多个虚函数。


  4. 类继承自一个基类,这个基类有1个或者多个虚函数。

所以在原来的代码中A不属于上面4类,所以编译器不会为它生成一个缺省拷贝构造函数。

我把我的理解写在了这个答案里。

Contents