拷贝构造函数与赋值构造函数(operator=)的区别
- 格式:pdf
- 大小:316.95 KB
- 文档页数:2
详解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++在单继承、多继承、虚继承时,构造函数、复制构造函数、赋值操作符、析构函数的执⾏顺序和执⾏内容⼀、本⽂⽬的与说明1. 本⽂⽬的:理清在各种继承时,构造函数、复制构造函数、赋值操作符、析构函数的执⾏顺序和执⾏内容。
2. 说明:虽然复制构造函数属于构造函数的⼀种,有共同的地⽅,但是也具有⼀定的特殊性,所以在总结它的性质时将它单独列出来了。
3. 单继承、多继承、虚继承,既然都属于继承,那么虽然有⼀定的区别,但还是相同点⽐较多。
如果放在⼀块讲,但为了将内容制作成递进的,就分开了,对相同点进⾏重复,(⼤量的复制粘贴哈),但在不同点进⾏了标注。
注意:三块内容是逐步递进的如果你懂虚函数,那么单继承和多继承那块你就可以不看;如果你懂多继承,那单继承你就不要看了,⾄于虚继承就等你懂虚继承再回来看吧;如果你只懂单继承,那你就只看单继承就好。
⼆、基本知识1. 对于⼀个空类,例如:class EmptyClass{};虽然你没有声明任何函数,但是编译器会⾃动为你提供上⾯这四个⽅法。
class EmptyClass {public:EmptyClass(); // 默认构造函数EmptyClass(const EmptyClass &rhs); // 复制构造函数~EmptyClass(); // 析构函数EmptyClass& operator=(const EmptyClass &rhs); // 赋值运算符}对于这四个⽅法的任何⼀个,你的类如果没有声明,那么编译器就会⾃动为你对应的提供⼀个默认的(注意合成默认构造函数是⽤于没有编写构造函数编译器才会合成默认构造函数,其中复制构造函数也是构造函数)。
(在《C++ primer》中,这个编译器⾃动提供的版本叫做“合成的***”,例如合成的复制构造函数)当然如果你显式声明了,编译器就不会再提供相应的⽅法。
2. 合成的默认构造函数执⾏内容:如果有⽗类,就先调⽤⽗类的默认构造函数。
复制构造函数与赋值操作符copy constructor 和copy assignment operator调用时间不一样,假设A同时拥有两者,b是A的一个实例:A a(b)与A a=b 调用得失copy constructorA a;a=b;调用的是assignment operator关于copy constructor和copy assignment的细微区别分类:C++2011-05-12 09:39 54人阅读评论(0) 收藏举报//写法1:T c = a+b;//写法2:T c;c = a+b;对于写法1,编译器会产生一个临时对象,放置a+b。
然后调用c的copy constructor把临时对象当做c的初始值://编译器附加代码:T _temp; //产生临时对象_temp = a+b; //放置a+b的值T c(_temp); //调用c的copy constructor还有一种可能是直接以拷贝构造的方式,将a+b的值放到c中。
T c(a+b);或者实行NRV优化:_result.T::T();//直接计算_result...C++standard允许编译器厂商有完全的自由度,但是由于市场的竞争,几乎保证任何表达式如果有这种形式:T c = a+b;那实现时根本不需要产生一个临时对象。
但是对于写法2,不能忽略临时对象。
它会导致下面结果:T temp;temp.operator+(a,b);c.operator=(temp);temp.T::~T();注意,此时在写法1中的"直接以拷贝构造方式来去除临时对象"或"以NRV优化来消除临时对象"的方法被作用于临时对象temp上。
导致只能消除T temp = a+b此表达式的临时对象,而真正的临时对象temp无法被消除。
不管哪一种情况,直接传递c到运算符函数是有问题的。
由于运算符函数并不为外加参数调用一个destructor(它期望一块新鲜的内存),所以必须在此调用前先调用destructor。
拷贝构造和赋值构造你知道什么叫“拷贝构造”和“赋值构造”吗?别急,我来慢慢给你讲。
这两个词儿听起来挺复杂,但它们就像是我们生活中的两个小细节,理解了其实挺简单的。
你看啊,拷贝构造就像你把自己喜欢的照片给朋友复印一份,赋值构造呢,就是你把手头的东西“借”给别人,然后自己又把东西还给自己的过程。
对了,我说的这些你可千万别误会,咱们可不是在聊复印机和借书馆,而是在聊 C++ 编程里的“类”对象的复制和赋值。
让我们先来说说拷贝构造。
拷贝构造的意思呢,就是用已有的一个对象来初始化一个新的对象。
比如你有一个苹果,嗯,还是个特别好的苹果,你想把它复制成另一个一模一样的苹果。
于是你给它复印了一下,结果出来的那个苹果长得一模一样,颜色一样,味道也一样。
就好像你从一个旧手机上拿到了相同的新手机。
拷贝构造就是这种“复印”的过程。
其实啊,这就像你从你最好的朋友那里借了一件超好看的衣服,然后穿上就发现,哇,跟自己的一模一样,简直成了“复制人”。
你知道,C++ 编程中这个拷贝构造器挺重要的,万一你不小心搞错了,你的代码就可能会出现一些不该发生的错误。
想象一下,原本应该是轻松愉快的代码就像是吃了个翻车的苹果,事儿就大了。
赋值构造又是啥呢?赋值构造呢,简单来说就是用一个已经存在的对象来给另一个对象赋值。
这跟拷贝构造有点儿像,但又不完全一样。
赋值构造就像是你已经有了一个苹果,然后你决定把这个苹果的味道、颜色和一切都赋给另一个苹果。
这时候,两个苹果就变得一模一样了,哪怕它们本来是两个独立的个体。
赋值构造有点儿像是你买了两双鞋,决定把一双送给你的朋友,你的朋友穿上之后就和你一模一样,走到哪儿都能“秀”出你们的默契。
这个过程也看似简单,但千万不要小看它,弄得不好,有时候会让你在不经意间犯个大错,就像你以为自己穿的是全新的鞋子,结果脚底下突然有个小洞,慌得一批。
说到这里,可能你会有个问题,拷贝构造和赋值构造它们不是差不多嘛?别着急,我来告诉你,虽然它们看上去挺像,但可大有不同。
c++赋值构造函数C++赋值构造函数(Copy Constructor)在 C++ 中,赋值构造函数(Copy Constructor)使用起来很方便。
当一个对象被初始化或者被赋值为另一个对象时,赋值构造函数会自动地执行。
这种初始化或者赋值一般是按值进行的,即用一个对象的所有成员变量的值来初始化或者赋值给另一个对象的成员变量。
有时候,在自定义的类中,按值赋值并不能满足要求。
这时可以自己定义赋值构造函数,以便能够对类变量进行更多的操作。
赋值构造函数是一种特殊的构造函数,它用来初始化一个对象,其参数是另一个同类型的对象。
当一个对象被赋值时,其生存期结束之前,就会调用赋值构造函数。
如果一个对象没有定义赋值构造函数,C++ 会使用默认的赋值构造函数,即将一个对象的所有参数复制到新的对象中。
而如果一个类定义了自己的赋值构造函数,则以用户自己的定义为准,C++ 不会再提供默认的方法。
通常情况下,赋值构造函数用于在创建一个新对象并将其内容初始化为另一个对象时使用。
在 C++ 中,赋值构造函数必须定义为 public 成员函数。
赋值构造函数的语法如下:ClassName (const ClassName& obj) {//}在上面的代码中,obj 是用来初始化一个新对象的对象。
在拷贝构造函数中,应该将obj 的所有成员变量的值复制到当前对象中。
注意事项:1、赋值构造函数通常按引用方式传递对象,以便避免无限递归调用构造函数而导致崩溃的问题。
2、赋值构造函数必须被声明为 const,否则会出现编译错误。
3、赋值构造函数可以在返回值前加上关键词 explicit,这样会规定编译器只使用显式调用,而不进行隐式调用,以免在意外的情况下以错误的方式进行转换。
下面是一个传递了引用参数的 C++ 赋值构造函数的例子,它是类的成员函数:在上述代码中,我们定义了一个名为 Line 的类,其中包括三个公有成员函数和一个私有成员。
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++类的拷贝、赋值与销毁(拷贝构造函数、拷贝赋值运算符和析构函数)拷贝构造函数如果⼀个构造函数的第⼀个参数是⾃⾝类类型的引⽤,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。
拷贝构造函数第⼀个参数必须是⼀个引⽤类型。
此参数⼏乎总是⼀个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");//编译器略过了拷贝构造函数。
拷贝构造函数和operator=拷贝构造函数和赋值运算符是C++中用于处理对象拷贝和赋值的重要概念。
让我来详细解释一下:拷贝构造函数是一个特殊的构造函数,它用于创建一个新对象,该对象的内容和另一个同类对象一样。
当使用一个对象去初始化另一个对象时,拷贝构造函数会被调用。
它通常的声明形式是`ClassName(const ClassName &obj)`,其中`ClassName`是类的名称。
拷贝构造函数的作用是创建一个新对象,并将另一个对象的值复制给新对象,以确保新对象和原对象是独立的。
赋值运算符(operator=)用于将一个对象的值赋给另一个对象。
它的作用是在已经存在的对象中赋予新的值。
赋值运算符通常被重载,以便在类中使用自定义的赋值操作。
它的通用形式是`ClassName& operator=(const ClassName &obj)`,其中`ClassName`是类的名称。
赋值运算符的重载允许我们在对象之间进行赋值操作,确保对象之间的数据正确复制。
需要注意的是,拷贝构造函数和赋值运算符的实现都需要考虑深拷贝和浅拷贝的问题。
深拷贝是指在拷贝构造函数和赋值运算符中,对动态分配的内存进行复制,从而使得两个对象完全独立。
而浅拷贝只是简单地复制对象的指针,导致两个对象共享同一块内存,容易引发错误。
在实现拷贝构造函数和赋值运算符时,我们需要确保正确地管理对象的资源,避免内存泄漏和悬挂指针等问题。
此外,如果类中包含指针成员变量,还需要考虑深拷贝和浅拷贝的区别,以及适当地重载拷贝构造函数和赋值运算符。
总之,拷贝构造函数和赋值运算符在C++中是非常重要的概念,正确地实现和使用它们可以确保对象的正确复制和赋值,避免出现潜在的问题。
希望我对拷贝构造函数和赋值运算符的解释能够帮助到你。