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

构造函数是干什么的

该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作,故:构造函数的作用:初始化对象的数据成员。

构造函数的种类

复制代码

11 class Complex 2 2 { 3 3 4 4 private : 5 5 double m_real; 6 6 double m_imag; 7 7 8 8 public: 9 9 1010 // 无参数构造函数 1111 // 如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做 1212 // 只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显示地写出来 1313 Complex(void) 1414 { 1515 m_real = 0.0; 1616 m_imag = 0.0; 1717 } 1818 1919 // 一般构造函数(也称重载构造函数) 2020 // 一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理) 2121 // 例如:你还可以写一个 Complex( int num)的构造函数出来 2222 // 创建对象时根据传入的参数不同调用不同的构造函数 2323 Complex(double real, double imag) 2424 { 2525 m_real = real; 2626 m_imag = imag; 2727 } 2828 2929 // 复制构造函数(也称为拷贝构造函数) 3030 // 复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中 3131 // 若没有显示的写复制构造函数,则系统会默认创建一个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因请查询有关 “浅拷贝” 、“深拷贝”的文章论述 3232 Complex(const Complex & c) 3333 { 3434 // 将对象c中的数据成员值复制过来 3535 m_real = c.m_real; 3636 m_img = c.m_img; 3737 } 3838 3939 // 类型转换构造函数,根据一个指定的类型的对象创建一个本类的对象 4040 // 例如:下面将根据一个double类型的对象创建了一个Complex对象 4141 Complex::Complex(double r) 4242 { 4343 m_real = r; 4444 m_imag = 0.0; 4545 } 4646 4747 // 等号运算符重载 4848 // 注意,这个类似复制构造函数,将=右边的本类对象的值复制给等号左边的对象,它不属于构造函数,等号左右两边的对象必须已经被创建 4949 // 若没有显示的写=运算符重载,则系统也会创建一个默认的=运算符重载,只做一些基本的拷贝工作 5050 Complex &operator=(const Complex &rhs) 5151 { 5252 // 首先检测等号右边的是否就是左边的对象本,若是本对象本身,则直接返回 5353 if ( this == &rhs ) 5454 { 5555 return *this; 5656 } 5757 5858 // 复制等号右边的成员到左边的对象中 5959 this->m_real = rhs.m_real; 6060 this->m_imag = rhs.m_imag; 6161 6262 // 把等号左边的对象再次传出 6363 // 目的是为了支持连等 eg: a=b=c 系统首先运行 b=c 6464 // 然后运行 a= ( b=c的返回值,这里应该是复制c值后的b对象) 6565 return *this; 6666 } 6767 };

复制代码

 下面使用上面定义的类对象来说明各个构造函数的用法:

复制代码

11 void main() 2 2 { 3 3 // 调用了无参构造函数,数据成员初值被赋为0.0 4 4 Complex c1,c2; 5 5 6 6 // 调用一般构造函数,数据成员初值被赋为指定值 7 7 Complex c3(1.0,2.5); 8 8 // 也可以使用下面的形式 9 9 Complex c3 = Complex(1.0,2.5); 1010 1111 // 把c3的数据成员的值赋值给c1 1212 // 由于c1已经事先被创建,故此处不会调用任何构造函数 1313 // 只会调用 = 号运算符重载函数 1414 c1 = c3; 1515 1616 // 调用类型转换构造函数 1717 // 系统首先调用类型转换构造函数,将5.2创建为一个本类的临时对象,然后调用等号运算符重载,将该临时对象赋值给c1 1818 c2 = 5.2; 1919 2020 // 调用拷贝构造函数( 有下面两种调用方式) 2121 Complex c5(c2); 2222 Complex c4 = c2; // 注意和 = 运算符重载区分,这里等号左边的对象不是事先已经创建,故需要调用拷贝构造函数,参数为c2 2323 2424 }

复制代码

参考:http://www.cnblogs.com/xkfz007/archive/2012/05/11/2496447.html

复制构造函数

几个原则:

C++ primer p406 :复制构造函数是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显示使用复制构造函数。当该类型的对象传递给函数或从函数返回该类型的对象时,将隐式调用复制构造函数。

C++支持两种初始化形式:复制初始化(int a = 5;)和直接初始化(int a(5);)对于其他类型没有什么区别,对于类类型直接初始化直接调用实参匹配的构造函数,复制初始化总是调用复制构造函数,也就是说:

A x(2);  //直接初始化,调用构造函数
A y = x;  //复制初始化,调用复制构造函数

必须定义复制构造函数的情况:

只包含类类型成员或内置类型(但不是指针类型)成员的类,无须显式地定义复制构造函数也可以复制;有的类有一个数据成员是指针,或者是有成员表示在构造函数中分配的其他资源,这两种情况下都必须定义复制构造函数。

什么情况使用复制构造函数:

类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
(1)一个对象以值传递的方式传入函数体 
(2)一个对象以值传递的方式从函数返回 
(3)一个对象需要通过另外一个对象进行初始化。

深拷贝和浅拷贝:

所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷贝。在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间

如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝

上面提到,如果没有自定义复制构造函数,则系统会创建默认的复制构造函数,但系统创建的默认复制构造函数只会执行“浅拷贝”,即将被拷贝对象的数据成员的值一一赋值给新创建的对象,若该类的数据成员中有指针成员,则会使得新的对象的指针所指向的地址与被拷贝对象的指针所指向的地址相同,delete该指针时则会导致两次重复delete而出错。下面是示例:

复制代码

11 #include <iostream.h> 2 2 #include <string.h> 3 3 class Person 4 4 { 5 5 public : 6 6 7 7 // 构造函数 8 8 Person(char * pN) 9 9 { 1010 cout << "一般构造函数被调用 !\n"; 1111 m_pName = new char[strlen(pN) + 1]; 1212 //在堆中开辟一个内存块存放pN所指的字符串 1313 if(m_pName != NULL) 1414 { 1515 //如果m_pName不是空指针,则把形参指针pN所指的字符串复制给它 1616 strcpy(m_pName ,pN); 1717 } 1818 } 1919 2020 // 系统创建的默认复制构造函数,只做位模式拷贝 2121 Person(Person & p) 2222 { 2323 //使两个字符串指针指向同一地址位置 2424 m_pName = p.m_pName; 2525 } 2626 2727 ~Person( ) 2828 { 2929 delete m_pName; 3030 } 3131 3232 private : 3333 char * m_pName; 3434 }; 3535 3636 void main( ) 3737 { 3838 Person man("lujun"); 3939 Person woman(man); 4040 4141 // 结果导致 man 和 woman 的指针都指向了同一个地址 4242 4343 // 函数结束析构时 4444 // 同一个地址被delete两次 4545 } 4646 4747 4848 // 下面自己设计复制构造函数,实现“深拷贝”,即不让指针指向同一地址,而是重新申请一块内存给新的对象的指针数据成员 4949 Person(Person & chs); 5050 { 5151 // 用运算符new为新对象的指针数据成员分配空间 5252 m_pName=new char[strlen(p.m_pName)+ 1]; 5353 5454 if(m_pName) 5555 { 5656 // 复制内容 5757 strcpy(m_pName ,chs.m_pName); 5858 } 5959 6060 // 则新创建的对象的m_pName与原对象chs的m_pName不再指向同一地址了 6161 }

复制代码

重载赋值操作符:

通过定义operate=的函数,可以对赋值进行定义。像其他任何函数一样,操作符函数有一个返回值和形参表。形参表必须具有与该操作符操作数书目相同的形参(如果操作符是一个成员,则包括隐式this形参)。赋值是二元运算,所以该操作符函数有两个形参:第一个形参(隐含的this指针)对应着左操作数,第二个形参对应右操作数。

 一个应用了对赋值号重载的拷贝构造函数的例子:

复制代码

11 #include <iostream> 2 2 3 3 using namespace std; 4 4 5 5 class A 6 6 { 7 7 public: 8 8 A(int);//构造函数 9 9 A(const A &);//拷贝构造函数 1010 ~A(); 1111 void print(); 1212 int *point; 1313 A &operator=(const A &); 1414 }; 1515 1616 A::A(int p) 1717 { 1818 point = new int; 1919 *point = p; 2020 } 2121 2222 A::A(const A &b) 2323 { 2424 *this = b; 2525 cout<<"调用拷贝构造函数"<<endl; 2626 } 2727 2828 A::~A() 2929 { 3030 delete point; 3131 } 3232 3333 void A::print() 3434 { 3535 cout<<"Address:"<<point<<" value:"<<*point<<endl; 3636 } 3737 3838 A &A::operator=(const A &b) 3939 { 4040 if( this != &b) 4141 { 4242 delete point; 4343 point = new int; 4444 *point = *b.point; 4545 } 4646 } 4747 4848 4949 int main() 5050 { 5151 A x(2); 5252 A y = x; 5353 x.print(); 5454 delete x.point; 5555 y.print(); 5656 5757 return 0; 5858 }

复制代码

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

java13天

构造函数一个对象的建立,构造函数只运行一次。而一般方法可以被该对象调用多次。personp2newperson();//初始化构造函数p2.p2content();//调用函数的内容,可重复调用构造代码块中定义的是不同对象共性的初始化内容classtest{{System.out.prin

C++ 析构函数与内存池

CPrimer书中也提到编写class时要注意copycontrol成员(拷贝构造函数,赋值操作符,析构函数,C11又多个移动构造函数)。工作时在C和C之间切换,有时就忘记了C的细节(真的好讨厌)。C析构函数与构造函数对应,构造对象时调用构造函数,析构对象时调用析构函数,于是可以在对象的析构函数中释放资

JS 对象数组Array 根据对象object key的值排序sort,很风骚哦

有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak