重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
对于普通类型的对象来说,它们之间的复制是很简单的,例如:
int a=88;
int b=a;
double f=3.12;
double d(f);
成都创新互联是一家集网站建设,绿春企业网站建设,绿春品牌网站建设,网站定制,绿春网站建设报价,网络营销,网络优化,绿春网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。
而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种数据成员。
#include
using namespace std;
class Test
{
private:
int a,b;
public:
Test(int x, int y) //提供的形式参数,是为了给数据成员直接初始化的
{
a=x;
b=y;
}
Test(const Test& C) //复制构造函数,提供一个同类型对象作为参数
{
a=C.a;
b=C.b;
}
void show ()
{
cout<
从以上代码的运行结果可以看出,系统在声明对象b和c时,完成了由对象a的复制。
复制构造函数,就类对象而言,相同类型的类对象是通过复制构造函数来完成整个复制过程的。
上例中的Test::Test(const Test& C),就是我们自定义的复制构造函数。可见,复制构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,用来约束作为参数的对象在构造新对象中是不能被改变的。
当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,复制构造函数就会被自动调用。也就是说,当类的对象需要复制时,复制构造函数将会被调用。以下情况都会调用复制构造函数:
一个对象以值传递的方式传入函数体
一个对象以值传递的方式从函数返回
一个对象需要通过另外一个对象进行初始化。
如果在类中没有显式地声明一个复制构造函数,那么,编译器将会自动生成一个默认的复制构造函数,该构造函数完成对象之间的浅复制,后面将进行说明。
自定义复制构造函数是一种良好的编程风格,它可以阻止编译器形成默认的复制构造函数,提高源码效率。
浅复制和深复制
所谓浅复制,如同上面出现过的构造函数中处理的一样,直接为数据成员赋值即可。在很多情况下,这是可以的。创建新的对象,要为对象的数据成员分配存储空间,直接赋值就将值保存在相应的空间中。然而,这种浅复制,却并不能通行天下,下面的程序中,浅复制带来了问题。
#include
#include
using namespace std;
class Test
{
private:
int a;
char *str;
public:
Test(int b, char *s)
{
a=b;
strcpy(str,s); //肇事地点,但不是祸端
}
Test(const Test& C)
{
a=C.a;
strcpy(str,C.str);
}
void show ()
{
cout<
程序运行中,会弹出一个窗口:程序的执行意外停止了。面对这个窗口,我们应该有感觉,这和使用指针不当有关系。
我们从main函数看起。
当程序执行到第28行Test a(100,"hello");时,对象a的数据成员a获得实际参数100的值,而数据成员str,即指针,是个随机地址值(指针的值,非指针指向的值)!在程序中,试图通过strcpy(str,s);将形式参数s指向的字符串"hello",复制到str所指向的那个位置,而那个位置,其地址并不是经过系统分配来的,这是个危险的操作。在这里,str这样未经过分配的地址(指针),被称为“野指针”。
在执行第29行Test b(a);时,同样的事情还要发生。
str这样的野指针是多么的霸道!在有的系统里,这样的行为是被接受的,可以想到其有多危险。有些系统中,不允许这样的事情发生的。于是,上面的程序在codeblock中调试会出错。这个错来的好。
解决这样的问题的方法,就是在构造函数中,要为指针类型的成员,分配专门的空间。以这条规则构建的复制,称作为深复制!
上面的程序,改写为:
#include
#include
using namespace std;
class Test
{
private:
int a;
char *str;
public:
Test(int b, char *s)
{
a=b;
str=new char[strlen(s)+1]; //分配str指向的空间,其长度根据s指向的字符串定。为何加1?字符串结束要用\0
strcpy(str,s); //前一程序的肇事地点,祸端已经由上一句摘除
}
Test(const Test& C)
{
a=C.a;
str=new char[strlen(C.str)+1]; //同上,这样str就不是野指针了
strcpy(str,C.str);
}
~Test()
{
delete []str;
}
void show ()
{
cout<
#include
using namespace std;
class A
{
private:
int arrayAddr;//保存一个有len个整型元素的数组的首地址
int len; //记录动态数组的长度
public:
A(int a, int n);
~A();
int sum();
};
A::A(int *a, int n)
{
len=n;
arrayAddr=new int[n]; //为指针数据成员分配空间,注意,没有上面例子中加1那回事
for(int i=0; i
arrayAddr[i]=a[i];
}
}
//析构函数的类外定义,释放指针型数据a所指向的空间
A::~A()
{
delete [] arrayAddr;
}
int A::sum() //获得a指向的数组中下标为i的元素的值
{
int s=0;
for(int i=0; i
s+=arrayAddr[i];
}
return s;
}
int main(){
int b[10]= {75, 99, 90, 93, 38, 15, 5, 7, 52, 4};
A r1(b,10);
cout<<"和:"<
A r2(c,15);
cout<<"和:"<
}