转自:http://hi.baidu.com/kxw102/blog/item/22d78c5dc436654afaf2c072.html
今天在学习的过程中注意到自己对于副本构造函数的理解还是需要提高的。副本构造函数也是编译器默认给我加上的一个构造函数,它在这种时候会发生:当你给对象声明中传入了相同的类的对象的时候会发生。举个例子说明一下。
Box.h
#pragma once
class Box
{
public:
int l,w,h; //在C++的Field中不需要初始化变量的.
Box(int inl,int inw,int inh);//我们只定义了这一个构造函数;
void showVolumn();
~Box(void);
};
Box.cpp
#include<iostream>
#include "Box.h"
using namespace std;
Box::Box(int inl, int inw, int inh):l(inl),w(inw),h(inh)
//这里采用的默认参数列表的方法来初始化内部数据成员l,w,h的
{
cout<<"Box constructor is called."<<endl;
}
void Box::showVolumn()
{
cout<<"The Length is: "<<l<<endl;
cout<<"The Width is: "<<w<<endl;
cout<<"The Height is: "<<h<<endl;
cout<<"The volumn of your Box is: "<<l*w*h<<endl;
}
Box::~Box(void)
{
}
MainTest.cpp
#include<iostream>
#include"Box.h"
using namespace std;
int main()
{
Box firstBox(10,20,30);
firstBox.showVolumn();
cout<<endl<<"This is the second Box"<<endl;
Box secondBox(firstBox);//这个时候就会用到系统默认的副本构造函数
secondBox.showVolumn();
system("PAUSE");
return 0;
}
输出的结果是这样的:
从结果中我们可以看出来,两个盒子的体积是一样的,而且最关键的两点就是:首先我们并没有定义这种输入是对象的构造函数,而系统给我们加上了这个默认的构造函数;再次构造函数只是调用了一次。
这种机制和没有定义构造函数是的情形是一样的,编译器会提供一个默认的构造函数来创建对象,这里是编译器生成了所谓的副本构造函数的默认版本。副本构造函数会复制输入的对象中的每一个成员,以创建对象。是完全的复制,所以不用经过原类的构造函数。副本构造函数可以用这种方法复制任何数据类型。
默认的副本构造函数适合于一些简单的类的定义,如上面的小例子,但是如果类的成员中如果有指针变量的话,就可能会出现大的错误。所以我们可以尝试着自己去定义了这个默认的副本构造函数,而不用系统给我们提供的。请看下面的例子,我们在类中定义一个指针,然后看看会发生什么奇怪的事情:
Box.h
#pragma once
class Box
{
public:
int l,w,h;//在C++的Field中不需要初始化变量的。
char *pchar;//在此处添加了一个内部指针。
Box(int inl,int inw,int inh);
void showVolumn();
void showChar();
~Box(void);
};
Box.cpp
#include<iostream>
#include "Box.h"
using namespace std;
Box::Box(int inl, int inw, int inh):l(inl),w(inw),h(inh)
//这里采用的默认参数列表的方法来初始化内部数据成员l,w,h的
{
pchar="Hello, I am a pointer.";
cout<<"Box constructor is called."<<endl;
}
void Box::showVolumn()
{
cout<<"The Length is: "<<l<<endl;
cout<<"The Width is: "<<w<<endl;
cout<<"The Height is: "<<h<<endl;
cout<<"The volumn of your Box is: "<<l*w*h<<endl;
}
void Box::showChar()
{
cout<<pchar<<endl;
}
Box::~Box(void)//很重要,此处添加了析构函数
{
delete pchar;//在此处我把pchar所指的内容删除掉了。
}
MainTest.cpp
#include<iostream>
#include"Box.h"
using namespace std;
int main()
{
Box *pBox1=new Box(10,20,30);
pBox1->showVolumn();
pBox1->showChar();
cout<<endl<<"This is the second Box"<<endl;
//Box secondBox(*pBox);//这个就是系统默认的副本构造函数
Box *pBox2=new Box(*pBox1);
pBox2->showVolumn();
pBox2->showChar();
delete pBox1;//在这个地方,把第一个指针所指的内容释放了,所以导致程序问题。
pBox2->showVolumn();
pBox2->showChar();
system("PAUSE");
return 0;
}
结果如下:
程序相当于执行到delete pBox1;以后就无法执行了,因为指针指向的内容没有了,所以程序无法执行下去。
基于上面所说的副本构造函数的问题,为了防止这个系统默认的副本构造函数的发生,我们就应该自己专门去指定一个副本构造函数:
假设我们先这样做,在Box.cpp中写下如下定义
Box::Box(Box boxObj)
{
.......
}
看起来似乎是对的,而且编译器也可以通过,但是呢,由于这个是输入参数是按值传送的,所以编译器要调用副本构造函数,制作参数的副本。但让,副本构造函数的参数是安值传送的,于是需要在此调用副本构造函数,这样不断重复下去。简而言之,这是一个副本构造函数的第归调用,形成一个无法中断的过程。
所以我们在定义副本构造函数的时候要用Const引用参数来定义,如下:
Box::Box(const Box &boxObj)
{
.......
}
由于现在参数传递不是传值,所以不用制作参数的副本,所以就防止了第归的问题。所以这就是我们的标准格式。
{
.......
}