拷贝构造函数

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

柔情痞子 提交于 2020-01-07 20:44:06
这里我们用类String 来介绍这两个函数: 拷贝构造函数是一种特殊构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用拷贝构造函数。为啥形参必须是对该类型的引用呢?试想一下,假如形参是该类的一个实例,由于是传值参数,我们把形参复制到实参会调用拷贝构造函数,如果允许拷贝构造函数传值,就会在拷贝构造函数内调用拷贝构造函数,从而形成无休止的递归调用导致栈溢出。 string(const string &s); //类成员,无返回值 赋值函数,也是赋值操作符重载,因为赋值必须作为类成员,那么它的第一个操作数隐式绑定到 this 指针,也就是 this 绑定到指向左操作数的指针。因此,赋值操作符接受单个形参,且该形参是同一类类型的对象。右操作数一般作为const 引用传递。 string& operator=(const string &s); //类成员,返回对同一类类型(左操作数)的引用 拷贝构造函数和赋值函数并非每个对象都会使用,另外如果不主动编写的话,编译器将以“位拷贝”的方式自动生成缺省的函数。在类的设计当中,“位拷贝”是应当防止的。倘若类中含有指针变量,那么这两个缺省的函数就会发生错误。这就涉及到深复制和浅复制的问题了。 拷贝有两种:深拷贝,浅拷贝 当出现类的等号赋值时,会调用拷贝函数

浅析C#深拷贝与浅拷贝

青春壹個敷衍的年華 提交于 2019-12-28 16:45:04
1.深拷贝与浅拷贝 拷贝即是通常所说的复制(Copy)或克隆(Clone),对象的拷贝也就是从现有对象复制一个“一模一样”的新对象出来。虽然都是复制对象,但是不同的复制方法,复制出来的新对象却并非完全一模一样,对象内部存在着一些差异。通常的拷贝方法有两种,即深拷贝和浅拷贝,那二者之间有何区别呢?MSDN里对IClone接口的Clone方法有这样的说明:在深层副本中,所有的对象都是重复的;而在浅表副本中,只有顶级对象是重复的,并且顶级以下的对象包含引用。可以看出,深拷贝和浅拷贝之间的 区别在于是否复制了子对象。 这如何理解呢?下面我通过带有子对象的代码来验证二者的区别。 首先定义两个类型:Student和ClassRoom,其中Student类型里包含ClassRoom,并使这两个类型都分别实现自定义的深拷贝接口(IDeepCopy)和浅拷贝接口(IShallowCopy)。 类图如下: 定义代码如下: 定义代码 /// <summary> /// 深拷贝接口 /// </summary> interface IDeepCopy { object DeepCopy(); } /// <summary> /// 浅拷贝接口 /// </summary> interface IShallowCopy { object ShallowCopy(); } /// <summary> ///

拷贝构造函数的调用

孤街醉人 提交于 2019-12-27 16:30:33
参考如下文章: 拷贝构造函数什么时候调用? 补充: 当一个函数中形参和返回值都是对象,理论上应该调用两次拷贝构造函数,但是实际上只调用了一次。编译器为了提高效率把这两次合并了。 如: # include <iostream> # include <string> # include <vector> using namespace std ; class B { private : int data ; public : B ( ) { cout << "default constructor" << endl ; } B ( const B & other ) { cout << "kaobei" << endl ; } ~ B ( ) { cout << "destructed" << endl ; } B ( int i ) : data ( i ) { cout << "constructed by parameter" << data << endl ; } } ; B Play ( B b ) { //函数中形参和返回值都是对象 return b ; } int main ( ) { B temp = Play ( 5 ) ; return 0 ; } //输出结果: constructed by parameter5 kaobei destructed

auto_ptr

会有一股神秘感。 提交于 2019-12-27 07:38:46
C++的auto_ptr auto_ptr所做的事情,就是动态分配对象以及当对象不再需要时自动执行清理。 1 构造函数与析构函数 auto_ptr在构造时获取对某个对象的所有权(ownership),在析构时释放该对象。我们可以这样使用auto_ptr来提高代码安全性: int* p = new int(0); auto_ptr<int> ap(p); 从此我们不必关心应该何时释放p, 也不用担心发生异常会有内存泄漏。 这里我们有几点要注意: 1) 因为auto_ptr析构的时候肯定会删除他所拥有的那个对象,所有我们就要注意了,一个萝卜一个坑,两个auto_ptr不能同时拥有同一个对象。像这样: int* p = new int(0); auto_ptr<int> ap1(p); auto_ptr<int> ap2(p); 因为ap1与ap2都认为指针p是归它管的,在析构时都试图删除p, 两次删除同一个对象的行为在C++标准中是未定义的。所以我们必须防止这样使用auto_ptr. 2) 考虑下面这种用法: int* pa = new int[10]; auto_ptr<int> ap(pa); 因为auto_ptr的析构函数中删除指针用的是delete,而不是delete [],所以我们不应该用auto_ptr来管理一个数组指针。 3)

C++之构造函数拷贝

蓝咒 提交于 2019-12-20 03:30:26
拷贝构造函数,顾名思义,就是通过拷贝对象的方式创建一个新对象。拷贝构造函数有两种原型(我们继续以book类来说明拷贝构造函数原型): book(book &b); book(const book &b); //下面一种原型则规定在创建新对象的时候不得修改被拷贝的对象 这两种原型都是book类对象的引用。下面一种原型则规定在创建新对象的时候不得修改被拷贝的对象。 如果拷贝构造函数的参数不是对象的引用,则是不允许的 。如下面这种构造函数形式则是无法编译通过的。 book(book b); //无法编译过去 为什么拷贝构造函数的参数一定要是对象的引用呢?我们可以想一下,如果不是引用,而是通过传值的方式将实参传递给形参,这中间本身就要经历一次对象的拷贝的过程,而对象拷贝则必须调用拷贝构造函数,如此一来则会形成一个死循环,无解。所以拷贝构造函数的参数必须是对象的引用。 拷贝构造函数除了能有对象引用这样的参数之外,同样也能有其它参数。但是其它参数必须给出默认值。例如下面这种拷贝构造函数声明方式。 book(const book &b, price = 5.0); 如果类的设计人员不在类中显示的声明一个拷贝构造函数,则系统会自动地为类生成一个拷贝构造函数, 自动生成的拷贝构造函数功能简单,只能将源对象的所有成员变量 一一复制给当前创建的对象。 class book { public: book(

C++之拷贝构造函数

别来无恙 提交于 2019-12-19 23:33:16
为什么要引入拷贝构造函数?(提出问题) 作用: 创建一个对象的同时,使用一个已经存在的对象给另一个对象赋值 做比较: 拷贝构造函数:对象被创建 + 用一个已经存在的对象 进行初始化 拷贝赋值函数:对象已经存在不用创建 + 用一个已经存在的对象进行初始化(区分开初始化操作和赋值) 举例: string a("hello");//调用构造函数 string b("would");//调用构造函数 string c=a;//调用拷贝构造函数--风格差,应使用string c(a) c=b;//调用拷贝赋值函数 什么时候使用拷贝构造函数?(系统自己调用) 在创建新对象的时候,希望将一个已经存在的对象拷贝给这个新对象,这时系统会自动调用拷贝构造函数 总结: 1、拷贝构造函数的参数必须是引用,否则出错。 2、执行的语句类似 Coord p=p1; 则会调用拷贝构造函数 有三种情况: 1)创建一个新类 + 并使用类的一个对象初始化该类的另一个对象 Coord p2(p1);//用对象p1初始化对象p2 Coord p3=p1;//用对象p1初始化对象p1 2)函数的形参是类的对象 + 参数使用值传递(参数为引用的时候不调用拷贝构造函数),传参时,会调用拷贝构造函数 fun1(Coord p) { 函数体 } 调用语句: Coord p1; fun1(p1); //分析:调用拷贝构造函数

Java深拷贝浅拷贝

青春壹個敷衍的年華 提交于 2019-12-18 18:46:51
首先, Java 中常用的拷贝操作有三个, operator = 、拷贝构造函数 和 clone() 方法。由于 Java 不支持运算符重载,我们无法在自己的自定义类型中定义 operator= 。拷贝构造函数大家应该很熟悉,现在看一下如何支持 clone 方法: 实现 Cloneable 接口,因为 Object 的 clone 方法将检查类是否实现了 Cloneable 接口,如果没有将抛出异常 CloneNotSupportedException 对象。 Cloneable 接口没有任何方法,只是个标志,所以只需要简单的写上 implements Cloneable 即可。 改写从 Object 继承而来的 clone 方法,使它的访问权限为 public ,因为为了防止意外的支持 clone 操作, Object 的 clone 方法是 protected 权限。 通过上面的分析,可以看出,如果我们要给自己的类添加拷贝功能,我们可以添加拷贝构造函数和实现 Cloneable 接口。 现在,来看一下不同的类型在拷贝过程中的表现: Operator = 拷贝构造函数 clone 方法 预定义非集合类型 深拷贝 如果支持拷贝构造函数的类型,则是深拷贝 不支持 自定义类型 浅拷贝 取决于实现 取决于实现 预定义集合类型 浅拷贝 会逐个调用每个元素的 operator= 方法

C++复制构造函数以及赋值操作符

守給你的承諾、 提交于 2019-12-10 04:12:49
当定义一个新类型的时候,需要显式或隐式地指定复制、赋值和撤销该类型的对象时会发生什么——这是通过定义特殊成员:复制构造函数、赋值操作符和析构函数来达到的。如果没有显式定义复制构造函数或者赋值操作符,编译器(通常)会为我们定义。 看看编译器默认的构造函数和赋值操作符做了些什么工作: class_a.h #ifndef _CLASS_A_H #define _CLASS_A_H class A { public: A(); A(int a); private: int a; }; #endif class_a.cpp #include "class_a.h" A::A(): a(0) { } A::A(int a) { this->a = a; } main.cpp #include "class_a.h" int main() { A obj1; //调用无形参构造函数 A obj2(2); //形参为int的构造函数 A obj3(obj1); //默认复制构造函数 A obj4 = 3; //调用形参为int的构造函数产生类A的临时变量,调用默认赋值操作符进行赋值 A obj5 = obj1; //调用默认赋值操作符 return 0; } 编译执行g++ -g main.cpp class_a.cpp 反汇编objdump -S a.out 可知:

构造函数与虚构函数

倖福魔咒の 提交于 2019-12-09 22:27:45
1、构造函数和析构函数为什么没有返回值? 构造函数和析构函数是两个非常特殊的函数:它们没有返回值。这与返回值为void的函数显然不同,后者虽然也不返回任何值,但还可以让它做点别的事情,而构造函数和析构函数则不允许。 在程序中创建和消除一个对象的行为非常特殊,就像出生和死亡,而且 总是由编译器来调用这些函数以确保它们被执行 。如果它们有返回值,要么编译器必须知道如何处理返回值,要么就只能由客户程序员自己来显式的调用构造函数与析构函数,这样一来,安全性就被破坏了。另外, 析构函数不带任何参数 ,因为析构不需任何选项。 如果允许构造函数有返回值,在某此情况下,会引起歧义。如下两个例子 class C { public: C(): x(0) { } C(int i): x(i) { } private: int x; }; 如果C的构造函数可以有返回值,比如: int:int C():x(0) { return 1; } //1表示构造成功,0表示失败 那么下列代码会发生什么事呢? C c = C(); //此时c.x == 1 分析: 很明显,C()调用了C的无参数构造函数,该构造函数返回int值1。 按照C++的规定,C c = C();是用默认构造函数创建一个临时对象,并用这个临时对象初始化c,此时,c.x的值应该是0。 但是,如果C::C()有返回值,并且返回了1(为了表示成功)

C++拷贝构造函数心得

空扰寡人 提交于 2019-12-09 21:06:01
C++Primer作者提到拷贝构造函数调用的三种时机: 1. 当用一个类对象去初始化另外一个类对象(类似于 AClass aInstance = bInstance),这里不是调用赋值构造函数(也叫赋值重载运算符)。复制是说初始化,赋值是初始化后重新覆盖旧值 2.函数形参是类对象 3.函数返回值为对象,函数执行完返回时会生成一个临时对象,将值复制给临时对象 但是下列代码验证时发现 第三个返回值是对象时并没有调用拷贝构造函数 class ZooAnimal { public: ZooAnimal() : loc (1){} ZooAnimal(const ZooAnimal &lhs) { this->loc = lhs.loc; cout << "copy construct" << endl; } virtual ~ZooAnimal() { cout << "deconstructer" << endl; } virtual void rotate() { cout << "ZooAnimal" << endl; cout << loc << endl; } public: int loc; string name; }; ZooAnimal func() { ZooAnimal x;     x,loc = 2; return x; } int main(int argc,