C++之拷贝构造

拷贝构造

  1. 拷贝构造
  2. 初始化列表

什么是拷贝构造?

拷贝函数,它是一种特殊的构造函数,主要用来完成一些基于同一类的其他对象的构建及初始化,用自身这种类型的对象来构造自身。

拷贝构造的定义

  1. 用户未定义拷贝构造
    系统默认提供一个隐式的拷贝构造,它会将存在于对象中的数据成员逐个的拷贝到新创建的对象中。
  2. 用户主动定义拷贝构造
    类名(const 类名& 引用名){}
    在函数体内用户可以自行决定需要执行的操作过程。

拷贝构造的示例

class CProp
    {
      char*pName;
    public:
      CProp(){}		//无参构造(默认构造)
      CProp(int money){}	//有参构造
      //拷贝构造  在这个里面把传进来的对象一一赋值给新的对象
      CProp(const CProp &pr)      
      {
	    pName=new char[strlen(pr.pName)+1];
	    strcpy(pName,pr.pName);
      }
    };    

拷贝构造的调用

  1. 用一个类对象去初始化该类的另一个对象时
	CProp pr1;
	CProp pr2(pr1);		//拷贝构造的显式调用
	CProp pr3 = pr1;		//拷贝构造的隐式调用
	CProp *pPr = new CProp(pr1);	//拷贝构造的显式调用
  1. 如果函数的形参是类的对象,以值传递的方式进行实参传递时,调用拷贝构造
void MySample(CProp p);
  1. 如果一个函数的返回值是类对象类型,以值传递的方式返回时
	CProp GetProp()
	{
	    return p;
	}

拷贝构造引发的情况

  1. 浅拷贝

这个系统提供的构造,可以理解为用=号一个一个的赋值,那么在我们使用对象
的成员有指针申请内存的时候,那么我们就会遇到一个问题。如下:
在这里插入图片描述
会把A对象成员指针直接赋值给到B对象成员,这样并没有给B对象成员的指针来
重新申请内存保存内容,新的对象里面的指针和传进的指针都是指向同一块内存,
两个对象死亡时,都会调用各自的析构函数来释放内存,但是两个指针都是指向同
一个地址,那么第一个指针释放之后,第二个就没有释放的了,所以会出错。

  1. 深拷贝

给新的指针申请内存来存内容,这样就不会出现浅拷贝所出现的问题了。
如下:
在这里插入图片描述

  1. 什么时候应该使用深拷贝

类中有动态申请内存,必须要重写拷贝构造,来做深拷贝。
如下:
CProp是一个类,在这个类中有指针,该指针会在使用中申请内存,然后析构会
释放内存,如果没有重新定义拷贝构造,那使用:
CProp pr1;
CProp pr2=pr1;
这样就会有浅拷贝的问题

练习代码

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
    int a;
    char* name;
public:
    Person()
    {
        a = 10;
        name = new char[strlen("小叶") + 1];
        strcpy(name, "小叶");
        cout << "无参构造" << endl;
    }
    Person(int a, const char* name)
    {
        this->a = a;
        this->name = new char[strlen(name) + 1];
        strcpy(this->name, name);
        cout << "拷贝成功 !" << endl;
    }
    //拷贝构造
    Person(const Person& p)
    {
        a = p.a;
        name = new char[strlen(p.name) + 1];
        strcpy(name, p.name);
        cout << "带参构造" << endl;
    }
    void show()
    {
        cout << "Id :" << a <<endl<< "Name :" << name<<endl;
    }
    ~Person()
    {
        if (name != NULL)
        {
            delete []name;
            name = NULL;
            cout << "析构函数" << endl;
        }
    }
};

//当返回值是一个对象的时候调用拷贝构造
Person fun()
{
    Person p(1, "Yesir");
    return p;
}
int main()
{
    /*Person p(1,"小叶");
    p.show();
    Person p2 = p;
    p2.show();*/

    //匿名函数, 生命周期只有一行
    //Person().show();
    //cout << "----------" << endl;
    //fun();
    system("pause");
    return 0;
}

初始化列表

构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表。

初始化列表的定义

class CTank
    {
	int att;
	int def;
	int hp;
    public:
	CTank():att(10),def(6),hp(200)	//初始化列表
	{
		...
	}
    };

初始化列表的特性

  1. 初始化列表也是实现类成员数据初始化的一种方式。
  2. 一些特殊的情况下,数据成员的初始化只能用初始化列表,而不能直接赋值。
  3. 初始化列表必须写在构造函数的定义体后面,用 :开头,表中用 ,分隔。
  4. 构造函数能对数据进行的操作,初始化列表也可以。反之不一定。
  5. 被初始化的成员是按照他们在类中出现的顺序来进行初始化,而不是按照他们在初始化列表中出现的顺序来进行初始化。

初始化列表的示例

//第一种
class CObject
    {
    public:	 
        CObject(int x, int y, int z) :objectX(x), objectY(y){}
 	 /*CObject(int x, int y)
 	 {
 	     objectX = x;
 	     objectY = y;
 	 }*/
        int objectX, objectY;
    };
   //第二种
    class CNpc
    {
    public:	 
        CNpc(int h, int lev) :hp(h), level(lev){}
 	 /*CNpc(int h, int lev)
 	 {
 	     hp = h;
 	     level = lev;//错误,常量不能被修改
 	 }*/
        int hp;
	 const int level;
    }; 

第一种初始化列表的使用,和构造直接赋值一样效果x的值赋值给objectX,y的值赋值给objectY。
第二种初始化列表的使用,如果类里面有const常量成员必须要用初始化列表来进行赋值。

初始化列表和构造函数

  1. 初始化列表需要写在构造函数的定义后面。
  2. 初始化列表能完成构造函数不一定能完成的数据的初始化。
  3. 构造函数是函数,可以在函数体内实现对于函数的调用,初始化列表不能。

练习代码

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Person
{
    int a;
    char* name;
public:
    Person()
    {
        a = 10;
        name = new char[strlen("小叶") + 1];
        strcpy(name, "小叶");
        cout << "无参构造" << endl;
    }
    Person(int a, const char* name)
    {
        this->a = a;
        this->name = new char[strlen(name) + 1];
        strcpy(this->name, name);
        cout << "拷贝成功 !" << endl;
    }
    //拷贝构造
    Person(const Person& p)
    {
        a = p.a;
        name = new char[strlen(p.name) + 1];
        strcpy(name, p.name);
        cout << "带参构造" << endl;
    }
    void show()
    {
        cout << "Id :" << a <<endl<< "Name :" << name<<endl;
    }
    ~Person()
    {
        if (name != NULL)
        {
            delete []name;
            name = NULL;
            cout << "析构函数" << endl;
        }
    }
};

//当返回值是一个对象的时候调用拷贝构造
Person fun()
{
    Person p(1, "Yesir");
    return p;
}

//初始化列表
class Gad
{
    int a, b, c;
public:
    Gad(int x, int y, int z) :a(x), b(y), c(z)
    {
        a = 10;
        b = 20;
        c = 30;
        //先运行初始化列表, 再进入函数体内
    }
    void show()
    {
        cout << a << " " << b << " " << c << endl;
    }
};

class A
{
    const int a;
public:
    //常量在定义的时候没有初始化的话, 必须得使用初始化列表对其进行初始化
    A():a(10)
    {
         
    }
    void show()
    {
        cout << a << endl;
    }
    
};

class B
{
    int a;
public:
    B(int x):a(x)
    {
        cout << "B的带参构造" << endl;
    }
    ~B()
    {
        cout << "B的析构" << endl;
    }
};

class C
{
    B b;
    int a;
public:
    C(int x):b(10),a(x)
    {
        cout << "C的带参构造" << endl;
    }
    ~C()
    {
        cout << "C的析构" << endl;
    }
};
int main()
{
    /*Person p(1,"小叶");
    p.show();
    Person p2 = p;
    p2.show();*/

    //匿名函数, 生命周期只有一行
    //Person().show();
    //cout << "----------" << endl;

    //fun();
    B::B(12);
    /*Gad g(1, 2, 3);
    g.show();*/

    /*A a;
    a.show();*/

    //栈区: 先进后出, B->C->C->B
    //{C c(10); }
    system("pause");
    return 0;
}
  • 8
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值