C++之拷贝构造
拷贝构造
- 拷贝构造
- 初始化列表
什么是拷贝构造?
拷贝函数,它是一种特殊的构造函数,主要用来完成一些基于同一类的其他对象的构建及初始化,用自身这种类型的对象来构造自身。
拷贝构造的定义
- 用户未定义拷贝构造
系统默认提供一个隐式的拷贝构造,它会将存在于对象中的数据成员逐个的拷贝到新创建的对象中。- 用户主动定义拷贝构造
类名(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);}};
拷贝构造的调用
- 用一个类对象去初始化该类的另一个对象时
CProp pr1;CProp pr2(pr1); //拷贝构造的显式调用CProp pr3 = pr1; //拷贝构造的隐式调用CProp *pPr = new CProp(pr1); //拷贝构造的显式调用
- 如果函数的形参是类的对象,以值传递的方式进行实参传递时,调用拷贝构造
void MySample(CProp p);
- 如果一个函数的返回值是类对象类型,以值传递的方式返回时
CProp GetProp(){return p;}
拷贝构造引发的情况
- 浅拷贝
这个系统提供的构造,可以理解为用=号一个一个的赋值,那么在我们使用对象
的成员有指针申请内存的时候,那么我们就会遇到一个问题。如下:
会把A对象成员指针直接赋值给到B对象成员,这样并没有给B对象成员的指针来
重新申请内存保存内容,新的对象里面的指针和传进的指针都是指向同一块内存,
两个对象死亡时,都会调用各自的析构函数来释放内存,但是两个指针都是指向同
一个地址,那么第一个指针释放之后,第二个就没有释放的了,所以会出错。
- 深拷贝
给新的指针申请内存来存内容,这样就不会出现浅拷贝所出现的问题了。
如下:
- 什么时候应该使用深拷贝
类中有动态申请内存,必须要重写拷贝构造,来做深拷贝。
如下:
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) //初始化列表{...}};
初始化列表的特性
- 初始化列表也是实现类成员数据初始化的一种方式。
- 一些特殊的情况下,数据成员的初始化只能用初始化列表,而不能直接赋值。
- 初始化列表必须写在构造函数的定义体后面,用 :开头,表中用 ,分隔。
- 构造函数能对数据进行的操作,初始化列表也可以。反之不一定。
- 被初始化的成员是按照他们在类中出现的顺序来进行初始化,而不是按照他们在初始化列表中出现的顺序来进行初始化。
初始化列表的示例
//第一种
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常量成员必须要用初始化列表来进行赋值。
初始化列表和构造函数
- 初始化列表需要写在构造函数的定义后面。
- 初始化列表能完成构造函数不一定能完成的数据的初始化。
- 构造函数是函数,可以在函数体内实现对于函数的调用,初始化列表不能。
练习代码
#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;
}
更多推荐
C++之拷贝构造
发布评论