对于拷贝构造函数以及赋值构造函数的定义

合集下载

详解C++中构造函数,拷贝构造函数和赋值函数的区别和实现

详解C++中构造函数,拷贝构造函数和赋值函数的区别和实现

详解C++中构造函数,拷贝构造函数和赋值函数的区别和实现C++中⼀般创建对象,拷贝或赋值的⽅式有构造函数,拷贝构造函数,赋值函数这三种⽅法。

下⾯就详细⽐较下三者之间的区别以及它们的具体实现1.构造函数构造函数是⼀种特殊的类成员函数,是当创建⼀个类的对象时,它被调⽤来对类的数据成员进⾏初始化和分配内存。

(构造函数的命名必须和类名完全相同)⾸先说⼀下⼀个C++的空类,编译器会加⼊哪些默认的成员函数默认构造函数和拷贝构造函数析构函数赋值函数(赋值运算符)取值函数**即使程序没定义任何成员,编译器也会插⼊以上的函数!注意:构造函数可以被重载,可以多个,可以带参数;析构函数只有⼀个,不能被重载,不带参数⽽默认构造函数没有参数,它什么也不做。

当没有重载⽆参构造函数时,A a就是通过默认构造函数来创建⼀个对象下⾯代码为构造函数重载的实现<span style="font-size:14px;">class A{int m_i;Public:A(){Cout<<”⽆参构造函数”<<endl;}A(int i):m_i(i) {} //初始化列表}</span>2.拷贝构造函数拷贝构造函数是C++独有的,它是⼀种特殊的构造函数,⽤基于同⼀类的⼀个对象构造和初始化另⼀个对象。

当没有重载拷贝构造函数时,通过默认拷贝构造函数来创建⼀个对象A a;A b(a);A b=a; 都是拷贝构造函数来创建对象b强调:这⾥b对象是不存在的,是⽤a 对象来构造和初始化b的!!先说下什么时候拷贝构造函数会被调⽤:在C++中,3种对象需要复制,此时拷贝构造函数会被调⽤1. 1)⼀个对象以值传递的⽅式传⼊函数体2. 2)⼀个对象以值传递的⽅式从函数返回3. 3)⼀个对象需要通过另⼀个对象进⾏初始化什么时候编译器会⽣成默认的拷贝构造函数:1. 1)如果⽤户没有⾃定义拷贝构造函数,并且在代码中使⽤到了拷贝构造函数,编译器就会⽣成默认的拷贝构造函数。

C++拷贝(复制)构造函数详解

C++拷贝(复制)构造函数详解

C++拷贝(复制)构造函数详解⼀. 什么是拷贝构造函数⾸先对于普通类型的对象来说,它们之间的复制是很简单的,例如:[c-sharp]1. int a = 100;2. int b = a;⽽类对象与普通对象不同,类对象内部结构⼀般较为复杂,存在各种成员变量。

下⾯看⼀个类对象拷贝的简单例⼦。

[c-sharp]1. #include <iostream>2. using namespace std;3.4. class CExample {5. private:6. int a;7. public:8. //构造函数9. CExample(int b)10. { a = b;}11.12. //⼀般函数13. void Show ()14. {15. cout<<a<<endl;16. }17. };18.19. int main()20. {21. CExample A(100);22. CExample B = A; //注意这⾥的对象初始化要调⽤拷贝构造函数,⽽⾮赋值23. B.Show ();24. return 0;25. }运⾏程序,屏幕输出100。

从以上代码的运⾏结果可以看出,系统为对象 B 分配了内存并完成了与对象 A 的复制过程。

就类对象⽽⾔,相同类型的类对象是通过拷贝构造函数来完成整个复制过程的。

下⾯举例说明拷贝构造函数的⼯作过程。

[c-sharp]1. #include <iostream>2. using namespace std;3.4. class CExample {5. private:6. int a;7. public:8. //构造函数9. CExample(int b)10. { a = b;}11.12. //拷贝构造函数13. CExample(const CExample& C)14. {15. a = C.a;16. }17.18. //⼀般函数19. void Show ()20. {21. cout<<a<<endl;22. }23. };24.25. int main()26. {27. CExample A(100);28. CExample B = A; // CExample B(A); 也是⼀样的29. B.Show ();30. return 0;31. }CExample(const CExample& C) 就是我们⾃定义的拷贝构造函数。

拷贝构造和拷贝赋值

拷贝构造和拷贝赋值

拷贝构造和拷贝赋值拷贝构造和拷贝赋值是面向对象编程中常用的两个概念。

它们在C++、Java等编程语言中经常被用到,对于理解对象的复制和拷贝有着重要的作用。

拷贝构造是指通过已存在的对象创建一个新的对象,这个新对象和原对象的成员变量具有相同的值。

例如,我们有一个学生类(Student),其中包含学生的姓名、学号、年龄等成员变量。

当我们通过已存在的学生对象创建一个新的学生对象时,就需要使用拷贝构造。

拷贝构造函数在C++中常用的格式为:类名(const 类名& obj)。

在拷贝构造函数中,我们将传递进来的对象的成员变量值赋值给新创建的对象的成员变量。

拷贝构造函数的使用可以大大简化对象的创建过程,并且保证了新创建的对象和原对象之间的数据一致性。

同时,拷贝构造函数还可以用于对象作为函数参数进行传递,以及对象作为函数返回值返回的情况。

拷贝赋值是指将一个已存在的对象的值赋给另一个已经存在的对象。

在C++中,我们可以通过重载赋值操作符(=)来实现对象的拷贝赋值。

拷贝赋值的格式为:类名& operator=(const 类名& obj)。

在拷贝赋值中,我们将被赋值对象的成员变量值赋给目标对象的成员变量。

拷贝赋值操作符的使用可以方便地将一个对象的值拷贝给另一个对象,使得两个对象具有相同的值。

拷贝赋值操作符还可以用于链式赋值,即多个赋值操作符连续使用的情况。

无论是拷贝构造还是拷贝赋值,都是对象间的复制操作,但它们的触发时机和方式是不同的。

拷贝构造是在创建新对象时调用,而拷贝赋值是在已有对象进行赋值操作时调用。

需要注意的是,在使用拷贝构造和拷贝赋值的过程中,我们需要自行管理动态分配的资源(如堆内存),防止内存泄漏和悬垂指针的问题。

拷贝构造和拷贝赋值在实际的编程中有着广泛的应用。

它们不仅可以用于类的对象,还可以用于容器类、智能指针等等。

对于理解对象的复制、赋值和传递过程,以及保证程序的正确性和高效性,拷贝构造和拷贝赋值具有重要的指导意义。

C++中拷贝构造函数与赋值运算符使用

C++中拷贝构造函数与赋值运算符使用
通常情况下的简单对象 ,不使用额外系统资源 ,则无须 提供默认拷贝构造函数或默认赋值运算符. 但是 ,如果对象 使用 了系 统资 源 ,就必 须为类提 供拷贝 构造函 数和赋值 运 算符 ,否则会引起如下错误 :
(1) 资源重用 ,任 何一方的 变化都 会影 响另一 方 ,造成 结果错误 ,资源的归属权不清 .
char name [ 20] ;} ; class base2
{public : base2( char 3 pn =“noname”,int x = 0) :obj (pn) { num = x ; cou〈t〈“constructing2 = ”< <“ ”< < num < < endl ;} pro tect ed :
第 22 卷 第 2 期 2006 年 4 月
赤 峰学 院 学报 Journal of Chifeng College
Vol. 22 No. 2 Apr. 2006
C+ +中拷贝构造函数与赋值运算符使用
刘 燕
(赤峰学院 计算机科学与技术系 ,内蒙古 赤峰 024000)
摘 要 : , 在使用过程中非常容
int num ; basel obj ;} ; void main( ) { base2 obj21(“Randy”,12345) ; base2 obj22 = obj21 ;/ / 调用拷贝构造函数 base2 obj23 ; obj23 = obj21 ;}/ / 调用赋值运算符 2. 1 例 1 中 base1 类和 base2 类均没有 提供拷贝 构造函数 和赋值 运 算 符 ,运 行 程 序 会 得 到 正 确 的 结 果 . 构 造 对 象 obj22 时 ,系统先调用 类成 员的 默认拷 贝构造 函数 ,完 成类 成员的拷贝工作 ,然后 再调 用组 合类 的默 认拷 贝构 造函 数 , 完成其它成员的拷贝 工作 . 对象 obj23 被赋值时 ,两 个类均 调用了默认赋值运算符 . 2.2 若在例 1 中只为 base1 类提 供拷 贝构 造函 数和赋值运 算符如下 : base 1 ( base 1 &s) { strcpy(name ,s. name) ; cout < <“copy constructingl”< < name < < endl ;} base 1 &operator = (base 1 &s) { strcpy( name ,s. name) ; cout < <“operator = ”< < name < < endl ; return 3 this;

C++拷贝构造函数和赋值构造函数

C++拷贝构造函数和赋值构造函数

C++拷贝构造函数和赋值构造函数转⾃:⼀、拷贝构造函数int main(int argc, char * argv[]){CExample A;A.Init40);CExample B=A; //把B初始化为A的副本...}B = A ; 此语句的具体过程:⾸先建⽴对象theObjtwo,并调⽤其构造函数,然后成员被拷贝。

语句"CExample B=A;" ⽤ A 初始化 B。

其完成⽅式是内存拷贝,复制所有成员的值。

完成后,A.pBuffer = B.pBuffer, 即它们将指向同样的地⽅,指针虽然复制了,但所指向的空间并没有复制,⽽是由两个对象共⽤了。

这样不符合要求,对象之间不独⽴了,并为空间的删除带来隐患。

所以需要采⽤必要的⼿段(拷贝构造函数)来避免此类情况。

拷贝构造函数的格式为 : 构造函数名(对象的引⽤) 提供了拷贝构造函数后的CExample类定义为:class CExample{public :CExample(){pBuffer=NULL; nSize=0;} //构造函数~CExample(){delete pBuffer;} // 析构函数CExample(const CExample&); //拷贝构造函数void Init(int n){ pBuffer=new char [n]; nSize=n;}private :char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源int nSize;};//拷贝构造函数的定义CExample::CExample(const CExample& RightSides){nSize=RightSides.nSize; //复制常规成员pBuffer=new char [nSize]; //复制指针指向的内容memcpy(pBuffer,RightSides.pBuffer,nSize*sizeof (char ));}这样,定义新对象,并⽤已有对象初始化新对象时,即执⾏语句“CExample B=A; ” 时,CExample(const CExample& RightSides)将被调⽤,⽽已有对象⽤别名RightSides传给构造函数,以⽤来作复制。

拷贝构造函数和赋值函数

拷贝构造函数和赋值函数

拷贝构造函数和赋值函数拷贝构造函数和赋值函数是C++中两个重要的函数,它们都是用来复制对象的。

拷贝构造函数用于创建一个新对象并将其初始化为另一个对象的副本,而赋值函数则用于将一个对象的值赋给另一个对象。

在本文中,我们将详细讨论这两个函数的作用和使用方法。

一、拷贝构造函数拷贝构造函数是一个特殊的构造函数,它用于创建一个新对象并将其初始化为另一个对象的副本。

当我们使用一个对象来初始化另一个对象时,拷贝构造函数就会被调用。

例如:```class MyClass {public:MyClass(); // 默认构造函数MyClass(const MyClass& other); // 拷贝构造函数// ...};MyClass obj1; // 调用默认构造函数MyClass obj2(obj1); // 调用拷贝构造函数```在上面的代码中,当我们使用obj1来初始化obj2时,拷贝构造函数就会被调用。

拷贝构造函数的参数是一个常量引用,它表示要复制的对象。

在拷贝构造函数中,我们可以使用另一个对象的数据来初始化新对象的数据成员,从而创建一个新的对象。

需要注意的是,如果我们没有定义拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数。

这个默认的拷贝构造函数会将一个对象的数据成员逐个复制到另一个对象中。

但是,如果我们的类中有指针类型的数据成员,那么默认的拷贝构造函数可能会出现问题,因为它只是简单地复制指针的值,而不是复制指针所指向的对象。

这时,我们就需要自己定义拷贝构造函数来保证正确的复制。

二、赋值函数赋值函数是用于将一个对象的值赋给另一个对象的函数。

当我们使用一个对象来赋值给另一个对象时,赋值函数就会被调用。

例如:```class MyClass {public:MyClass& operator=(const MyClass& other); // 赋值函数// ...};MyClass obj1, obj2;obj2 = obj1; // 调用赋值函数```在上面的代码中,当我们将obj1赋值给obj2时,赋值函数就会被调用。

【C++Primer第15章】定义派生类拷贝构造函数、赋值运算符

【C++Primer第15章】定义派生类拷贝构造函数、赋值运算符

【C++Primer第15章】定义派⽣类拷贝构造函数、赋值运算符学习资料••定义拷贝构造函数【注意】对派⽣类进⾏拷贝构造时,如果想让基类的成员也同时拷贝,就⼀定要在派⽣类拷贝构造函数初始化列表中显⽰调⽤基类拷贝构造函数(当然在函数体内将基类部分的值拷贝也是可以的,只不过它是先⽤默认构造函数初始化后再修改的基类成员变量的值,效率⽐较低),否则它会调⽤基类的默认构造函数,⽽不会对基类的成员变量拷贝值,这样⽣成的对象,它的派⽣类部分和被拷贝的对象派⽣类部分⼀样,⽽基类部分则是默认构造函数的初始化结果。

代码例⼦1:1 #include <iostream>2using namespace std;34class A5 {6public:7 A() { cout << "A default constructor" << endl; }8 A(A&) { cout << "A copy constructor" << endl; }9 };10class B : public A11 {12public:13 B() { cout << "A default constructor" << endl; }14 B(B &b) { cout << "B copy constructor" << endl; }15 };1617int main()18 {19 B b;20 B c = b;21return0;22 }输出结果:代码例⼦2:1 #include <iostream>2using namespace std;34class A5 {6public:7 A() { cout << "A default constructor" << endl; }8 A(A&) { cout << "A copy constructor" << endl; }9 };10class B : public A11 {12public:13 B() { cout << "A default constructor" << endl; }14 B(B &b) : A(b) { cout << "B copy constructor" << endl; }15 };1619 B b;20 B c = b;21return0;22 }输出结果:C++ 基类构造函数带参数的继承⽅式及派⽣类的初始化在定义类的时候,会遇到基类的构造函数带参数,⽽⼦类⼦类构造函数不带参数,这时候如果以代码 a 的⽅式建⽴派⽣类则会出错。

C++类构造函数、拷贝构造函数、复制构造函数、复制构造函数、构造函数显示调用和隐式调用

C++类构造函数、拷贝构造函数、复制构造函数、复制构造函数、构造函数显示调用和隐式调用

C++类构造函数、拷贝构造函数、复制构造函数、复制构造函数、构造函数显⽰调⽤和隐式调⽤⼀、构造函数是⼲什么的1.class Counter2.{3.public:4.// 类Counter的构造函数5.// 特点:以类名作为函数名,⽆返回类型6.Counter()7.{8.m_value = 0;9.}10.private:11.// 数据成员12.int m_value;13.}该类对象被创建时,编译系统对象分配内存空间,并⾃动调⽤该构造函数->由构造函数完成成员的初始化⼯作eg: Counter c1;编译系统为对象c1的每个数据成员(m_value)分配内存空间,并调⽤构造函数Counter( )⾃动地初始化对象c1的m_value值设置为0故:构造函数的作⽤:初始化对象的数据成员。

⼆、构造函数的种类1.class Complex2.{3.private :4.double m_real;5.double m_imag;6.public:7.Complex() //⼀般构造函数8.{9.m_real = 0.0;10.m_imag = 0.0;11.}12.13.Complex(double real, double imag) //⼀般构造函数14.{15.m_real = real;16.m_imag = imag;17.}18.19.Complex(const Complex & c) //拷贝构造函数20.{21.// 将对象c中的数据成员值复制过来22.m_real = c.m_real;23.m_imag = c.m_imag;24.}25.Complex &operator=( const Complex &rhs ) //赋值构造函数26.{27.// ⾸先检测等号右边的是否就是左边的对象本⾝,若是本对象本⾝,则直接返回28.if ( this == &rhs )29.{30.return *this;31.}32.// 复制等号右边的成员到左边的对象中33.this->m_real = rhs.m_real;34.this->m_imag = rhs.m_imag;35.// 把等号左边的对象再次传出36.return *this;37.}38.};// ⽆参数构造函数// 如果创建⼀个类你没有写任何构造函数,则系统会⾃动⽣成默认的⽆参构造函数,函数为空,什么都不做// 只要你写了⼀个下⾯的某⼀种构造函数,系统就不会再⾃动⽣成这样⼀个默认的构造函数,如果希望有⼀个这样的⽆参构造函数,则需要⾃⼰显⽰地写出来// ⼀般构造函数(也称重载构造函数)// ⼀般构造函数可以有各种参数形式,⼀个类可以有多个⼀般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理) // 例如:你还可以写⼀个 Complex( int num)的构造函数出来// 创建对象时根据传⼊的参数不同调⽤不同的构造函数// 复制构造函数(也称为拷贝构造函数)// 复制构造函数参数为类对象本⾝的引⽤,⽤于根据⼀个已存在的对象复制出⼀个新的该类的对象,⼀般在函数中会将已存在对象的数据成员的值复制⼀份到新创建的对象中// 若没有显⽰的写复制构造函数,则系统会默认创建⼀个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因请查询有关 “浅拷贝” 、“深拷贝”的⽂章论述// 类型转换构造函数,根据⼀个指定的类型的对象创建⼀个本类的对象,//需要注意的⼀点是,这个其实就是⼀般的构造函数,但是对于出现这种单参数的构造函数,C++会默认将参数对应的类型转换为该类类型,有时候这种隐私的转换是我们所不想要的,所以需要使⽤explicit来限制这种转换。

c++拷贝构造函数和赋值构造函数

c++拷贝构造函数和赋值构造函数

c++拷贝构造函数和赋值构造函数在C++中,对象的拷贝操作是很常见的。

对象的拷贝操作包括两种情况:一是对象的初始化,如“类名对象(obj1)=类名对象(obj2)”;二是对象的赋值,如“obj1=obj2”。

为了实现对象的拷贝操作,C++中提供了拷贝构造函数和赋值构造函数。

本文将详细介绍C++中的拷贝构造函数和赋值构造函数。

一、C++中的拷贝构造函数C++中的拷贝构造函数是一种特殊的构造函数,它用于完成一个对象到另一个对象的复制。

拷贝构造函数的定义形式如下:类名(const 类名& obj){}其中,类名为其所在类的名称,&表示引用类型,obj为其他的同类对象。

拷贝构造函数的目的是将一个对象复制到另一个对象中,使得两个对象完全相同。

下面是一个示例:class point{private:int x;int y;public:point(int x=0,int y=0):x(x),y(y){}point(const point& p):x(p.x),y(p.y){}//拷贝构造函数};在上面的代码中,point类中有一个拷贝构造函数,用于将对象p复制到当前对象中。

拷贝构造函数和普通构造函数在调用时是有区别的,普通构造函数传递的参数是要初始化的数据,而拷贝构造函数传递的参数是要复制的对象。

拷贝构造函数的使用场景有以下几种情况:1、类参数传递在函数参数中,可以通过拷贝构造函数将一个类对象传递到函数中。

如:void func(point p){cout<<p.get_x()<<","<<p.get_y()<<endl;}在上面的代码中,point类对象p被作为参数传递到func()函数中。

这里的拷贝构造函数用来完成将一个point对象(p)复制到另一个point对象中。

2、类返回值point func(){point p(1,2);return p;//将point对象p返回到主调函数中}在这里,point类的实例对象返回到主调函数中。

构造函数和析构函数和复制构造函数

构造函数和析构函数和复制构造函数

实验五【实验内容】构造函数和析构函数和复制构造函数构造函数1. 构造函数是类的一个特殊成员函数,它的函数名与类名相同。

2. 构造函数定义了对象数据成员的初始化方法,它只在创建对象时被系统自动调用,并且只在创建对象时调用一次,是为创建的对象分配内存空间和对数据成员进行初始化。

3. 构造函数无函数类型,没有返回值,一般都声明为公有函数。

4.C++规定,每个类必须有构造函数,没有构造函数就不能创建对象。

5.如果程序中未声明构造函数,则系统自动产生出一个默认形式的构造函数,如果提供了一个构造函数,系统就不能自动提供一个默认的构造函数。

6.构造函数可以重载,即可以定义多个参数及参数类型不同的构造函数。

复制构造函数又称拷贝构造函数,是一种特殊的构造函数,其形参为本类的对象引用。

功能是把初始值对象的每个数据成员的值都复制到新建立的对象。

如果程序员没有为类声明拷贝初始化构造函数,则编译器自己生成一个拷贝构造函数。

1. 当用类的一个对象去初始化该类的另一个对象时系统自动调用它实现拷贝赋值。

2. 若函数的形参为类对象,调用函数时,实参赋值给形参,系统自动调用拷贝构造函数。

3. 当函数的返回值是类对象时,系统自动调用拷贝构造函数。

析构函数析构函数也是特殊的类成员函数,它没有返回类型,没有参数,不能随意调用,也没有重载,只有在类的生命周期结束的时候,由系统自动调用。

析构函数和构造函数的最主要的区别:调用期不同、构造函数可以有参数可以重载。

实验1设计一个学生类stu,其中含有数据成员name、english和math,分别表示学生的姓名、英语成绩与数学成绩,使用类的成员函数output 和total输出学生的基本信息和总成绩,并完成在main函数中的相应调用。

#include <iostream>using namespace std;class stu{public:stu(char n[]="",double e=0.0,double m=0.0);~stu();void input();void output();void total();private:char name[8];double english,math;};stu::stu(char n[],double e,double m){strcpy(name,n);english=e;math=m;}stu::~stu(){}void stu::input(){cout<<"请输入学生的姓名、英语成绩与数学成绩:"<<endl;cin>>name>>english>>math;}void stu::output (){cout<<"学生的姓名、英语成绩与数学成绩:"<<name<<","<<english<<","<<math<<endl;}void stu::total (){cout<<"学生的总成绩是:"<<english+math<<endl;}void main(){stu s1("张思宁",95,80),s2;s2.input ();s1.output ();s1.total ();s2.output() ;s2.total ();}实验2练习书上112页例4-2,熟悉复制构造函数被调用的3种情况。

c拷贝构造函数和赋值构造函数

c拷贝构造函数和赋值构造函数

c拷贝构造函数和赋值构造函数C++编程语言中,拷贝构造函数和赋值构造函数是非常重要的函数,它们用来实现对象的拷贝和赋值操作,从而使得对象之间的数据和状态得以传递和共享。

下面我们将详细介绍这两种构造函数的定义、实现和应用。

一、拷贝构造函数拷贝构造函数是一种特殊的构造函数,它用于创建一个新的对象,该对象是原对象的一个副本,即它们具有相同的数据成员和属性值。

拷贝构造函数的定义格式如下所示:class ClassName {public:ClassName (const ClassName &obj) {}};在拷贝构造函数中,我们通过引用(const ClassName &obj)的方式来获取原对象的引用,然后将其成员变量值赋值给新对象的成员变量。

需要注意的是,拷贝构造函数不能直接调用,在以下情况下会被自动调用:1. 将一个对象作为函数参数传递给函数时2. 从函数返回对象3. 使用一个对象初始化另一个对象时以下是一个简单的例子,展示了如何实现一个带有拷贝构造函数的类:#include <iostream>using namespace std;class Box {public:int length;int width;int height;Box (int l=0, int w=0, int h=0) : length(l), width(w), height(h) {}Box (const Box &obj) : length(obj.length), width(obj.width), height(obj.height) {}};int main () {Box box1 (10, 5, 3);Box box2 = box1;cout << "Box 1: " << box1.length << ", " << box1.width << ", " << box1.height << endl;cout << "Box 2: " << box2.length << ", " << box2.width << ", " << box2.height << endl;return 0;}在上面的例子中,我们定义了一个拥有三个数据成员的Box类,该类有两个构造函数,一个默认构造函数,一个拷贝构造函数。

拷贝构造函数和赋值运算符的认识

拷贝构造函数和赋值运算符的认识

拷贝构造函数和赋值运算符的认识1. 拷贝构造函数⽣成新的类对象,⽽赋值运算符是给已有的对象重新赋值。

2. 由于拷贝构造函数是直接构造⼀个新的类对象,所以在初始化这个对象之前不⽤检验源对象是否和新对象相同。

⽽赋值运算符则需要这个操作,另外赋值运算中如果原来的对象中已有内存分配要先把内存释放掉。

3. *注意:当类中有指针类型的成员变量时,⼀定要重写拷贝构造函数和赋值运算发,不要使⽤默认的。

//copy constructor 声明string(const string&);//assign operator 声明string& operator=(const string& rhs);//拷贝构造函数的实现string::string(const string& another) {std::cout << "拷贝构造函数被调⽤:\n";m_data = new char[strlen(another.m_data) + 1];strcpy(m_data, another.m_data);}// 赋值运算string& string::operator=(const string& rhs) {std::cout << "赋值运算符被调⽤:\n";if (this == &rhs) return *this;delete[]m_data;m_data = new char[strlen(rhs.m_data) + 1];strcpy(m_data, rhs.m_data);return *this;}深拷贝、浅拷贝说到拷贝构造函数,就不得不提深拷贝和浅拷贝。

通常,默认⽣成的拷贝构造函数和赋值运算符,只是简单的进⾏值的复制。

例如:上⾯的Person类,字段只有int和string两种类型,这在拷贝或者赋值时进⾏值复制创建的出来的对象和源对象也是没有任何关联,对源对象的任何操作都不会影响到拷贝出来的对象。

对于拷贝构造函数和赋值构造函数的理解

对于拷贝构造函数和赋值构造函数的理解

对于拷贝构造函数和赋值构造函数的理解昨天晚上在看智能指针的时候,我发现⾃⼰连⼀个拷贝构造函数和赋值构造函数都写不出来,⾃⼰就尝试写了⼀个版本,结果发现错误百出,对于拷贝构造函数和赋值构造函数的理解仅仅停留在理论的⽅⾯,⽽不知其中太多的内涵。

⽐如我们都知道拷贝构造函数和赋值构造函数最⼤的不同在于:拷贝构造是确确实实构造⼀个新的对象,并给新对象的私有成员赋上参数对象的私有成员的值,新构造的对象和参数对象地址是不⼀样的,所以如果该类中有⼀个私有成员是指向堆中某⼀块内存,如果仅仅对该私有成员进⾏浅拷贝,那么会出现多个指针指向堆中同⼀块内存,这是会出现问题,如果那块内存被释放了,就会出现其他指针指向⼀块被释放的内存,出现未定义的值的问题,如果深拷贝,就不会出现问题,因为深拷贝,不会出现指向堆中同⼀块内存的问题,因为每⼀次拷贝,都会开辟新的内存供对象存放其值。

下⾯是浅拷贝构造函数的代码:#include <iostream>using namespace std;class A{private:int* n;public:A(){n = new int[10];n[0] = 1;cout<<"constructor is called\n";}A(const A& a){n = a.n;cout<<"copy constructor is called\n";}~A(){cout<<"destructor is called\n";delete n;}void get(){cout<<"n[0]: "<<n[0]<<endl;}};int main(){A* a = new A();A b = *a;delete a;b.get();return0;}运⾏结果如下:下⾯是深拷贝构造函数的代码:#include <iostream>#include <string.h>using namespace std;class A{private:int* n;public:A(){n = new int[10];cout<<"constructor is called\n";}A(const A& a){n = new int[10];memcpy(n, a.n, 10); //通过按字节拷贝,将堆中⼀块内存存储到另⼀块内存cout<<"copy constructor is called\n";}~A(){cout<<"destructor is called\n";delete n;}void get(){cout<<"n[0]: "<<n[0]<<endl;}};int main(){A* a = new A();A b = *a;delete a;b.get();return0;}运⾏截图如下:但是赋值构造函数是将⼀个参数对象中私有成员赋给⼀个已经在内存中占据内存的对象的私有成员,赋值构造函数被赋值的对象必须已经在内存中,否则调⽤的将是拷贝构造函数,当然赋值构造函数也有深拷贝和浅拷贝的问题。

C++复习笔记---浅谈拷贝构造函数和赋值构造函数

C++复习笔记---浅谈拷贝构造函数和赋值构造函数

C++复习笔记---浅谈拷贝构造函数和赋值构造函数1.拷贝构造函数的形式对于类X,如果它的函数形式如下a) X&b) const X&c) volatile X&d) const volatile X&且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数X::X(const X&);是拷贝构造函数X::X(const X&,int val = 10);是拷贝构造函数2.⼀个类中可以存在超过⼀个拷贝构造函数class X {public:X(const X&);X(X&); // OK};编译器根据实际情况调⽤const拷贝构造函数或⾮const的拷贝构造函数3.默认的拷贝构造函数⾏为a)先调⽤⽗类的拷贝构造函数b)如果数据成员为⼀个类的实例,则调⽤该类的拷贝构造函数c)其他成员按位拷贝4.默认的赋值构造函数⾏为a)先调⽤⽗类的赋值构造函数b)如果数据成员为⼀个类的实例,则调⽤该类的赋值构造函数c)其他成员按位拷贝5.提供显⽰的拷贝和赋值构造函数基本的原则是⼦类⼀定要调⽤⽗类的相应函数,参考⽅式Derive(const Derive& obj):Base(obj){…...}Derive& operator =(const Derive &obj){if ( this == &obj )return *this;//⽅式⼀Base::operator =(obj);//⽅式⼆static_cast<Base&>(*this) = obj;return *this;}另外当你的成员变量有const或者引⽤,系统⽆法为你提供默认的拷贝和赋值构造函数,我们必须⾃⼰处理这些特殊的情况。

c++类的拷贝、赋值与销毁(拷贝构造函数、拷贝赋值运算符和析构函数)

c++类的拷贝、赋值与销毁(拷贝构造函数、拷贝赋值运算符和析构函数)

c++类的拷贝、赋值与销毁(拷贝构造函数、拷贝赋值运算符和析构函数)拷贝构造函数如果⼀个构造函数的第⼀个参数是⾃⾝类类型的引⽤,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。

拷贝构造函数第⼀个参数必须是⼀个引⽤类型。

此参数⼏乎总是⼀个const的引⽤。

拷贝构造函数在⼏种情况下都会被隐式地使⽤。

因此,拷贝构造函数通常不应该是explicit的。

合成拷贝构造函数与合成默认构造函数不同,即使我们定义了其他构造函数,编译器也会为我们合成⼀个拷贝构造函数。

对某些类来说,合成拷贝构造函数⽤来阻⽌我们拷贝该类类型的对象。

⽽⼀般情况,合成的拷贝构造函数会将其参数的成员逐个拷贝到正在创建的对象中。

每个成员的类型决定了它如何拷贝。

拷贝初始化直接初始化和拷贝初始化的差异。

string dots(10,','); //直接初始化string s(dots); //直接初始化string s2 = dots; //拷贝初始化当使⽤直接初始化时,我们实际上是要求编译器使⽤普通的函数匹配来选择与我们提供的参数最匹配的构造函数。

当我们使⽤拷贝初始化时,我们要求编译器将右侧运算对象拷贝到正在创建的对象中,如果需要的话还要进⾏类型转换。

拷贝初始化通常使⽤拷贝构造函数来完成。

拷贝初始化是依靠拷贝构造函数或移动构造函数来完成的。

拷贝初始化不仅在我们⽤=定义变量时会发⽣,在下列情况下也会发⽣•将⼀个对象作为实参传递给⼀个⾮引⽤类型的形参。

•从⼀个返回类型为⾮引⽤类型的函数返回⼀个对象。

•⽤花括号列表初始化⼀个数组中的元素或⼀个聚合类中的成员。

参数和返回值拷贝构造函数被⽤来初始化⾮引⽤类类型参数,这⼀特性解释了为什么拷贝构造函数⾃⼰的参数必须是引⽤类型。

如果其参数不是引⽤类型,则调⽤永远也不会成功——为了调⽤拷贝构造函数,我们必须拷贝它的实参,但为了拷贝实参,我们⼜必须调⽤拷贝构造函数,如此⽆限循环。

拷贝初始化的限制vector<int> v1(10); //直接初始化vector<int> v1 = 10; //错误:接受⼤⼩参数的构造函数是explicit的如果我们希望使⽤⼀个explicit构造函数,就必须显式的使⽤:void f(vector<int>); //f的参数进⾏拷贝初始化f(10); //错误:不能⽤⼀个explicit的构造函数拷贝⼀个实参f(vector<int>(10)); //正确:从⼀个int直接构造⼀个临时vector如果我们希望使⽤⼀个explicit构造函数,就必须显式的使⽤:编译器可以绕过拷贝构造函数编译器被允许将下⾯的代码string null_book = "9-999-99999-9";给写成string null_book("9-999-99999-9");//编译器略过了拷贝构造函数。

移动构造函数和移动赋值与拷贝构造函数和赋值构造函数的比较

移动构造函数和移动赋值与拷贝构造函数和赋值构造函数的比较

移动构造函数和移动赋值与拷贝构造函数和赋值构造函数的⽐较⾸先看拷贝构造函数://拷贝构造函数A(A& t){if(t.text!=NULL){int len=strlen(t.text);text=new char[len+1];strcpy(text,t.text);}}拷贝构造函数中实现了深拷贝处理。

再看移动构造函数://移动构造函数A(A&& t){if(t.text!=NULL){text=t.text;t.text=NULL;}} 代码构造和拷贝构造函数类似,但是内存的处理不是拷贝⽽是转移。

注意参数类型是右值引⽤。

移动赋值运算符赋值运算符的情况和构造函数类似,还是先考察普通的赋值运算符://拷贝赋值运算符A& operator=(const A& rhs){if(this!=&rhs){free();if(rhs.text!=NULL){int len=strlen(rhs.text);text=new char[len+1];strcpy(text,rhs.text);}}return *this;}再看移动赋值运算符://移动赋值运算符A& operator=(A&& rhs)noexcept{if(this!=&rhs){free();text=rhs.text;rhs.text=NULL;}reeturn *this;}。

C++为什么拷贝构造函数参数必须为引用?赋值构造函数参数也必须为引用吗?

C++为什么拷贝构造函数参数必须为引用?赋值构造函数参数也必须为引用吗?

C++为什么拷贝构造函数参数必须为引⽤?赋值构造函数参数也必须为引⽤吗?之前写拷贝构造函数的时候,以为参数为引⽤,不为值传递,仅仅是为了减少⼀次内存拷贝。

然⽽今天看到⼀篇⽂章发现⾃⼰对拷贝构造的参数理解有误。

参数为引⽤,不为值传递是为了防⽌拷贝构造函数的⽆限递归,最终导致栈溢出。

下⾯来看⼀个例⼦:1. class test2. {3. public:4. test()5. {6. cout << "constructor with argument\n";7. }8. ~test()9. {10.11. }12. test(test& t)13. {14. cout << "copy constructor\n";15. }16. test&operator=(const test&e)17. {18. cout << "assignment operator\n";19. return *this;20. }21. };22. int _tmain(int argc, _TCHAR* argv[])23. {24. test ort;25. test a(ort);26. test b = ort ;27. a = b;28. return 0;29. }输出:如果这些知识你都能理解。

下⾯就来解释⼀下为什么值传递会⽆限递归!如果复制构造函数是这样的:1. test(test t);我们调⽤1. test ort;2. test a(ort); --> test.a(test t=ort)==test.a(test t(ort))3. -->test.a(test t(test t = ort))4. ==test.a(test t(test t(ort)))5. -->test.a(test t(test t(test t=ort)))6. ...7. 就这样会⼀直⽆限递归下去。

拷贝构造函数和赋值函数

拷贝构造函数和赋值函数

拷贝构造函数和赋值函数1、拷贝构造函数:⽤⼀个已经有的对象构造⼀个新的对象。

CA(const CA & c )函数的名称必须和类名称相⼀致,它的唯⼀的⼀个参数是本类型的⼀个引⽤变量,该参数是const 类型,不可变。

拷贝构造函数什么时候被⽤到?(1)当⽤⼀个已初始化过的⾃定义类型对象去初始化另⼀个新构造的对象的时候。

(2)⼀个对象以值传递的⽅式传⼊。

在调⽤函数时需要将实参对象完整的传递给形参,系统是通过调⽤拷贝构造函数来实现的,这样能保证形具有和实参完全相同的值。

(3)⼀个对象以值传递的⽅式传出函数体。

在函数调⽤完毕将返回值带回函数调⽤处。

此时需要将函数中的对象拷贝到⼀个临时对象处并传给该函数的调⽤处。

拷贝构造函数为什么传引⽤⽽不传值?如果拷贝构造函数进⾏值传递的话,那么形参会产⽣⼀个新对象,也就是会调⽤拷贝构造函数,因此会形成⼀个死循环。

调⽤拷贝构造函数需要先创建形参对象,创建形参对象⼜需要调⽤拷贝构造函数,⽆限递归肯定是不正确的,所以在这⾥要⽤引⽤⽽不是值传递。

2、赋值运算符的重载函数:⽤⼀个已经有的对象给⼀个同⼀类型的对象赋值。

(两个对象都存在)CA & operator = (const CA & )返回值的对象为引⽤是为了可以连续赋值赋值函数中可以既可以使⽤引⽤也可以使⽤值传递,不过值传递会多⽣成⼀个对象,造成资源的浪费。

class A{private:int a;int b;public:A (const A & arg) // 拷贝构造函数{a = arg.a;b = arg.b;}A & operator = ( const A & arg) //重载的赋值函数{if(this != &arg)// 先判断下是否是⾃⾝赋值{a = arg.a;b = arg.b;}return *this;}};当类中含有指针成员时,两者的意义有很⼤区别复制构造函数需为指针变量分配内存空间,并将实参的值拷贝到其中;⽽赋值操作符它实现的功能仅仅是将‘=’号右边的值拷贝⾄左值,在左边对象内存不⾜时,先释放然后再申请。

全国计算机等级考试二级:C++对象的拷贝与赋值操作

全国计算机等级考试二级:C++对象的拷贝与赋值操作

我发现⼀些同事在编写⼀个类时,知道什么时候需要实现拷贝构造函数和赋值操作,但不知道什么时候拷贝构造函数被调⽤,什么时候赋值操作被调⽤,甚⾄把⼆者混为⼀谈。

要弄明⽩这个问题,最简单的做法莫过于写个测试程序试⼀下。

不过那样做也未必是好办法,实验的结果往往导致以偏概全的结论。

不如好好想⼀下,弄清楚其中的原理,再去写程序去验证也不迟。

拷贝构造函数,顾名思义,等于拷贝 + 构造。

它肩负着创建新对象的任务,同时还要负责把另外⼀个对象拷贝过来。

⽐如下⾯的情况就调⽤拷贝构造函数:CString str = strOther; 赋值操作则只含有拷贝的意思,也就是说对象必须已经存在。

⽐如下⾯的情况会调⽤赋值操作。

str = strOther; 不过有的对象是隐式的,由编译器产⽣的代码创建,⽐如函数以传值的⽅式传递⼀个对象时。

由于看不见相关代码,所以不太容易明⽩。

不过我们稍微思考⼀下,就会想到,既然是根据⼀个存在的对象拷贝⽣成新的对象,⾃然是调⽤拷贝构造函数了。

两者实现时有什么差别呢?我想有⼈会说,没有差别。

呵,如果没有差别,那么只要实现其中⼀个就⾏了,何必要两者都实现呢?不绕圈⼦了,它们的差别是: 拷贝构造函数对同⼀个对象来说只会调⽤⼀次,⽽且是在对象构造时调⽤。

此时对象本⾝还没有构造,⽆需要去释放⾃⼰的⼀些资源。

⽽赋值操作可能会调⽤多次,你在拷贝之前要释放⾃⼰的⼀些资源,否则会造成资源泄露。

明⽩了这些道理之后,我们不防写个测试程序来验证⼀下我们的想法:#include#include#includeclass CString{public:CString();CString(const char* pszBuffer);~CString();CString(const CString& other);const CString& operator=(const CString& other);private:char* m_pszBuffer;;};CString::CString(){printf("CString::CString\n");m_pszBuffer = NULL;return;}CString::CString(const char* pszBuffer){printf("CString::CString(const char* pszBuffer)\n");m_pszBuffer = pszBuffer != NULL ? strdup(pszBuffer) : NULL;return;}CString::~CString(){printf("%s\n", __func__);delete m_pszBuffer;m_pszBuffer = NULL;return;}CString::CString(const CString& other){if(this == &other){return;}printf("CString::CString(const CString& other)\n");m_pszBuffer = other.m_pszBuffer != NULL ? strdup(other.m_pszBuffer) : NULL; }const CString& CString::operator=(const CString& other){printf("const CString& CString::operator=(const CString& other)\n");if(this == &other){return *this;}if(m_pszBuffer != NULL){free(m_pszBuffer);m_pszBuffer = NULL;}m_pszBuffer = other.m_pszBuffer != NULL ? strdup(other.m_pszBuffer) : NULL; return *this;}void test(CString str){CString str1 = str;return;}int main(int argc, char* argv[]){CString str;CString str1 = "test";CString str2 = str1;str1 = str;CString str3 = str3;test(str);return 0;}。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
相关文档
最新文档