看了《C++编程思想》的拷贝构造函数部分,反反复复看了几遍终于想通是怎么回事了,现在做个记录。
前提1:对于没有定义拷贝构造函数的类,编译器会为其创建一个默认的拷贝构造函数,只不过是最简单的位拷贝。
前提2:在执行函数的时候,先将函数参数压栈,再将返回地址压栈,然后再执行函数代码,产生局部变量,如下图。在函数最后,先把返回值赋给返回地址的变量,然后函数结束,销毁在函数中产生的局部变量。
前提3:编译器为个正确地计算一个表达式而需要一个临时对象时,编译器可以创建一个,但临时对象的存在时间应尽可能的短。
先看第一个例子
#include <fstream>
#include <string>
using namespace std;
ofstream out( "HowMany.txt" );
class HowMany
{
static int objectCount;
public:
HowMany(){ objectCount++; }
static void print( const string &msg = "" )
{
if ( msg.size() !=0 )
out<<msg<<": ";
out<<"objectCount = "<<objectCount<<endl;
}
~HowMany()
{
objectCount--;
print( "~HowMany()" );
}
};
int HowMany::objectCount = 0;
HowMany f( HowMany x )
{
x.print( "x argument inside f()" );
return x;
}
int main()
{
HowMany h;
HowMany::print( "after construction of h" );
HowMany h2 = f( h );
HowMany::print( "after print f()" );
}
输出文件中为:
after construction of h: objectCount = 1
x argument inside f(): objectCount = 1
~HowMany(): objectCount = 0
after print f(): objectCount = 0
~HowMany(): objectCount = -1
~HowMany(): objectCount = -2
直接看main函数
1、2行不解释了,其对应的输出为:
after construction of h: objectCount = 1
此时在内存中存在一个HowMany对象h
3行
根据前提2,先将参数和h2的地址(返回地址)压栈,由于函数f的参数是按值传递的,将h传递时调用拷贝构造函数(是默认的拷贝构造函数,前提1),拷贝给x,因为是位拷贝,所以objectCount并没有改变,输出为
x argument inside f(): objectCount = 1
内存中的对象:h,x
函数结束,根据前提2将x拷贝给h2,销毁局部变量x(析构函数,objectCount-1),输出为:
~HowMany(): objectCount = 0
内存中:h,h2
4行也不解释了,输出为:
after print f(): objectCount = 0
内存中:h,h2
最后程序结束,销毁内存中的h,h2(析构函数,objectCount--,先销毁h2),输出为:
~HowMany(): objectCount = -1
~HowMany(): objectCount = -2
以上是没有定义拷贝构造函数的情况,再来看一个定义了构造函数的例子
#include <fstream>
#include <string>
using namespace std;
ofstream out( "HowMany.txt" );
class HowMany2
{
string name;
static int objectCount;
public:
HowMany2( const string &id = "" ) : name( id )
{
objectCount++;
print( "HowMany2" );
}
~HowMany2()
{
objectCount--;
print( "~HowMany2()" );
}
HowMany2( const HowMany2 &p ) : name( p.name )
{
name += " copy";
++objectCount;
print( "HowMany2(const HowMany2 &)" );
}
void print( const string &msg = "" ) const
{
if ( msg.size() !=0 )
out<<msg<<endl;
out<<"\t"<<name<<": "<<"objectCount="<<objectCount<<endl;
}
};
int HowMany2::objectCount = 0;
HowMany2 f( HowMany2 x )
{
x.print( "x argument inside f()" );
out<<"Returning from f()"<<endl;
return x;
}
int main()
{
HowMany2 h( "h" );
out<<"Entering f()"<<endl;
HowMany2 h2 = f( h );
h2.print( "h2 after call to f()" );
out<<"Call f(),no return value"<<endl;
f( h );
out<<"After call to f()"<<endl;
}
输出文件中为:
HowMany2
h: objectCount=1
Entering f()
HowMany2(const HowMany2 &)
h copy: objectCount=2
x argument inside f()
h copy: objectCount=2
Returning from f()
HowMany2(const HowMany2 &)
h copy copy: objectCount=3
~HowMany2()
h copy: objectCount=2
h2 after call to f()
h copy copy: objectCount=2
Call f(),no return value
HowMany2(const HowMany2 &)
h copy: objectCount=3
x argument inside f()
h copy: objectCount=3
Returning from f()
HowMany2(const HowMany2 &)
h copy copy: objectCount=4
~HowMany2()
h copy: objectCount=3
~HowMany2()
h copy copy: objectCount=2
After call to f()
~HowMany2()
h copy copy: objectCount=1
~HowMany2()
h: objectCount=0
看main函数
1、2行不解释,输出为:
HowMany2 h: objectCount=1 Entering f()
内存中:h
3行,同样先将参数和返回地址压栈。由于HowMany2定义了自己的拷贝构造函数,调用该拷贝构造函数将h拷贝给局部变量x(objectCount++),然后执行函数,输出为:
HowMany2(const HowMany2 &) h copy: objectCount=2 x argument inside f() h copy: objectCount=2 Returning from f()
内存中:h、x
在函数最后,与第一个例子一样,将x拷贝给h2(objectCount++),销毁x(objectCount--),输出为:
HowMany2(const HowMany2 &) h copy copy: objectCount=3 ~HowMany2() h copy: objectCount=2
内存中:h、h2
4、5行输出为:
h2 after call to f() h copy copy: objectCount=2 Call f(),no return value
内存中:h、h2
6行,函数结束之前与第3行一样,输出为:
HowMany2(const HowMany2 &) h copy: objectCount=3 x argument inside f() h copy: objectCount=3 Returning from f()
内存中:h、h2、x
在函数的最后,将x拷贝给返回值,根据前提三,会创建一个临时变量,就叫他n吧,x拷贝给n后(objectCount++),销毁x(objectCount--),输出为:
HowMany2(const HowMany2 &) h copy copy: objectCount=4 ~HowMany2() h copy: objectCount=3
内存中:h、h2、n
因为临时变量n的存在时间应尽可能的短,所以紧接着销毁n(析构objectCount--),输出为:
~HowMany2() h copy copy: objectCount=2
内存中:h、h2
7行,输出为:
After call to f()
内存中:h、h2
程序结束,销毁h和h2(先销毁h2),输出为:
~HowMany2() h copy copy: objectCount=1 ~HowMany2() h: objectCount=0
终于解释完了,最后做一个总结,借用《C++编程思想》里的一句话:“拷贝构造函数实现了按值传递方式的参数传递和返回”。