拷贝构造
- 拷贝构造
- 初始化列表
什么是拷贝构造?
拷贝函数,它是一种特殊的构造函数,主要用来完成一些基于同一类的其他对象的构建及初始化,用自身这种类型的对象来构造自身。
拷贝构造的定义
- 用户未定义拷贝构造
系统默认提供一个隐式的拷贝构造,它会将存在于对象中的数据成员逐个的拷贝到新创建的对象中。- 用户主动定义拷贝构造
类名(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;
}