
之前说过, 当你不定义拷贝构造的时候,系统会给你自动生成一个。
这个自动生成的,就是浅拷贝。
对于这个例子。
如果存在
String a(b);或者String a=b;
这样的定义。
就会调用拷贝构造。用系统生成的那个,效果就是
aLength =bLength;
aSp = bSp;
也就是说,两个共用同一个Sp指针,这个指向的是一个分配的空间。
然后,一旦任何一个被析构了,比如b被析构,那么bSp被释放。
再去通过aSp访问这个空间,就会导致出错了。
但是在这个代码里面,并没有这样的定义,为什么一定要拷贝构造。。。
因为,operator+函数返回的并不是引用。
所以 这个函数实现的时候,一定是定义一个局部变量,把a+b的结果写进去,返回。
str3=str2+str1的时候,先一个无名临时变量,被拷贝构造出来,然后再调用
operator=重载,把这个无名临时变量赋值给str3
执行这个 *** 作后,无名临时变量就被析构了。
这样,如果没有拷贝构造的定义,进行深拷贝。
那么得到的str3Sp就是一个野指针。
在C++中,下面三种对象需要调用拷贝构造函数(有时也称“复制构造函数”):
1) 一个对象作为函数参数,以值传递的方式传入函数体;
2) 一个对象作为函数返回值,以值传递的方式从函数返回;
3) 一个对象用于给另外一个对象进行初始化(常称为赋值初始化);
如果在前两种情况不使用拷贝构造函数的时候,就会导致一个指针指向已经被删除的内存空间。对于第三种情况来说,初始化和赋值的不同含义是拷贝构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值 *** 作符共同实现的。描述拷贝构造函数和赋值运算符的异同的参考资料有很多。
通常的原则是:①对于凡是包含动态分配成员或包含指针成员的类都应该提供拷贝构造函数;②在提供拷贝构造函数的同时,还应该考虑重载=赋值 *** 作符号。原因详见后文。
拷贝构造函数必须以引用的形式传递(参数为引用值)。其原因如下:当一个对象以传递值的方式传一个函数的时候,拷贝构造函数自动的被调用来生成函数中的对象。如果一个对象是被传入自己的拷贝构造函数,它的拷贝构造函数将会被调用来拷贝这个对象这样复制才可以传入它自己的拷贝构造函数,这会导致无限循环直至栈溢出(Stack Overflow)。除了当对象传入函数的时候被隐式调用以外,拷贝构造函数在对象被函数返回的时候也同样的被调用。 如果在类中没有显式的声明一个拷贝构造函数,那么,编译器会自动生成一个来进行对象之间非static成员的位拷贝(Bitwise Copy)。这个隐含的拷贝构造函数简单的关联了所有的类成员。注意到这个隐式的拷贝构造函数和显式声明的拷贝构造函数的不同在于对成员的关联方式。显式声明的拷贝构造函数关联的只是被实例化的类成员的缺省构造函数,除非另外一个构造函数在类初始化或构造列表的时候被调用。
拷贝构造函数使程序更有效率,因为它不用再构造一个对象的时候改变构造函数的参数列表。设计拷贝构造函数是一个良好的风格,即使是编译系统会自动为你生成默认拷贝构造函数。事实上,默认拷贝构造函数可以应付许多情况。
引申:在这里,与C#是不同的。C#里面用已知的对象去初始化另一个对象,传递的是该已知对象的指针,而并不是隐式地拷贝构造函数。例如: using System;using SystemCollectionsGeneric;using SystemLinq;using SystemText;namespace C_Sharp_structVSclass{class MyClass{public intval;}struct myStruct{public intval;}class Program{static void Main(string []args){MyClass objectA = new MyClass();MyClass objectB = objectA;objectAval=10;objectBval=20;ConsoleWriteLine({0},objectAval);ConsoleWriteLine({0},objectBval);ConsoleReadKey();}}}这里的输出会是“20,20”而不是C++里面的10,20。所以一定要跟C++区分开看。
什么是拷贝构造函数?
是一种特殊的构造函数,特殊之处在于它有的时候会隐式调用
class A {
public:
A(int x, int y, ) // 普通构造函数
A(const A&) // 拷贝构造函数,一个参数必须是引用类型
};
当你有个函数
calculate(A a) {}
你调用的时候
A a_in_main() // 调用普通构造函数
calculate(a_in_main); // 这个调用因为是参数是A类型的(不是引用也不是指针),所以要拷贝这个对象,这时就会用a_in_main作为A(const A& other)的参数other,在函数calculate中构造一个新的A类对象。
在什么情况下必须实现拷贝构造函数?
因为你不忽略拷贝构造函数的时候,编译器会给你生成一个按数据位拷贝的一个默认版本,即把类的数据成员直接拷贝到新的对象中。这样如果成员有指针指向动态分配的内存,那么直接拷贝指针就会使两个对象中的指针指向同样的内存,一般情况下这种效果都不是想要的。还有一种说法是深拷贝浅拷贝,编译器给你生成的默认版本就是浅拷贝,不会管你指针指向的是什么,只会给你直接把指针的值复制过去。所以只要你需要深拷贝的时候,才需要实现拷贝构造函数。
const是可以不需要的,对于&这个,大部分的时候是要的,可以提高程序的效率,这是一个运算符,叫“引用”,拷贝构造函数也是系统自己调用的,用户是不调用的,当你在以这个类的对象为参数传递时,它不是以值来传递的,是以他自己本身来传递的,“引用”就是给那个对象取一个别名,其实就是他自己,这样的话就免去的先把值传递给一个局部的变量(这是要把这个对象中的值赋给这个变量的,也就是创立一个和本对象一模一样的另一个类对象,这就叫做拷贝,或者说是复制)。
对于const,这个是定义常量,是为了防止这个数据被修改而加上去的歌修饰符!很简单的,这个可以替代C里面的#define
宏定义,具有更好的安全性!
要还不懂就Hi我吧!
可以。
因为拷贝构造函数是放在本身这个类里的,而类中的函数可以访问这个类的对象的所有成员,当然包括私有成员了。
String(const String &other)
{
m_data=otherx;//x是String类的私有成员
}
引用参数对象也是String类的,所以没问题
在C++中,下面三种对象需要拷贝的情况。因此,拷贝构造函数将会被调用。
1). 一个对象以值传递的方式传入函数体
2). 一个对象以值传递的方式从函数返回
3). 一个对象需要通过另外一个对象进行初始化
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)