一、拷贝构造函数
1、用对象来初始化对象
- 简单变量定义时,可以直接初始化,也可以用另一个同类型变量来初始化。譬如:
int a = 6;
int b = a;
- 用class来定义对象时,可以直接初始化,也可以用另一个对象来初始化。譬如:
person p1(“zhangsan”,18,true); //person是一个class类
person p2(p1); //用p1对象来初始化p2对象
person p2 = p1; //效果等同于上面
2、为什么可以
- 变量的直接初始化,是变量在被分配内存之后直接用初始化值去填充赋值完成初始化。
- 变量用另一个变量来初始化,是给变量分配了内存后执行了一个内存复制操作来完成的初始化。
- 对象的直接初始化,是对象在分配内存之后调用了相应的constructor构造函数来完成的初始化。
- 对象用另一个对象来初始化,是对象在分配内存之后调用了相应的copy constructor拷贝构造函数来完成初始化。
3、拷贝构造函数
- 拷贝构造函数是构造函数的一种,符合构造函数的一般性规则。
- 拷贝构造函数的引入是为了让对象在初始化时能够像简单变量一样的被直接用=来赋值
- 拷贝构造函数不需要重载,它的参数列表固定为const classname& xx
- 拷贝构造函数很合适用初始化列表来实现
class person
{
// 访问权限
public:
// 属性
string name; // 名字
int age; // 年龄
bool male; // 性别,男为true,女为false
// int *pInt; // 只是分配了p本身的4字节内存,并没有分配p指向的空间内存
// 构造和析构
// person(){}; // 默认构造函数
// person(string myname); // 自定义构造函数
// person(string myname, int myage); // 自定义构造函数
person(string myname, int myage, bool mymale); // 自定义构造函数
// person(string myname = "aston", int myage = 33, bool mymale = false); //默认构造函数
person(const person& pn); // 拷贝构造函数
~person(); // 默认析构函数
// 方法
void eat(void);
void work(void);
void sleep(void);
void print(void);
private:
};
//自定义构造函数的实现
MAN::person::person(string myname, int myage, bool mymale):name(myname),age(myage),male(mymale)
{
// this->pInt = new int[100]; // 分配了100个元素的数组
cout << "userdefined constructor" << endl;
}
//拷贝构造函数的实现
MAN::person::person(const person& pn):name(pn.name),age(pn.age),male(pn.male)
{
/*
this->name = pn.name;
this->age = pn.age;
this->male = pn.male;
*/
cout << "copy constructor" << endl;
}
//析构函数的实现
MAN::person::~person()
{
// 默认析构函数是空的
// delete this->pInt; // 释放单个内存
// delete[] this->pInt; // 释放数组内存
cout << "userdefined destructor" << endl;
}
二、浅拷贝和深拷贝
1、浅拷贝的缺陷
- 只有普通成员变量初始化的拷贝构造函数,就是浅拷贝。
- 如果不显式提供,c++会自动提供一个全部普通成员被浅拷贝的默认copy constructor拷贝构造函数。
- 浅拷贝在遇到有动态内存分配时就会出问题。譬如class中定义一个int *pInt; pInt指向一块内存空间。
2、如何解决
- 不要用默认copy constructor,自己显式提供一个copy constructor,并且在其内部再次分配动态内存。
- 这就叫深拷贝,深的意思就是不止给指针变量本身分配内存,也给指针指向的空间再分配内存(如果有需要还要复制内存内的值)。
- 一般如果不需要深拷贝,根本就不用显式提供copy constructor,所以提供了的基本都是需要深拷贝的。
- 拷贝构造函数不需要额外的析构函数来对应,用的还是原来的析构函数
3、如何深度理解浅拷贝和深拷贝
- 这个问题不是c++特有的,Java等语言也会遇到,只是语言给封起来了,而c++需要class类作者自己精心处理。
- 从编程语言学角度讲,本质上是值语义value symatics 和 引用语义reference symatics的差别。
class person
{
// 访问权限
public:
// 属性
string name; // 名字
int age; // 年龄
bool male; // 性别,男为true,女为false
int *pInt; // 只是分配了p本身的4字节内存,并没有分配p指向的空间内存
// 构造和析构
// person(){}; // 默认构造函数
// person(string myname); // 自定义构造函数
// person(string myname, int myage);
person(string myname, int myage, bool mymale);
// person(string myname = "aston", int myage = 33, bool mymale = false);
person(const person& pn); // 拷贝构造函数
~person(); // 默认析构函数
// 方法
void eat(void);
void work(void);
void sleep(void);
void print(void);
private:
};
//默认构造函数
MAN::person::person(string myname, int myage, bool mymale):name(myname),age(myage),male(mymale)
//MAN::person::person(string myname, int myage, bool mymale)
{
this->pInt = new int(5);
cout << "userdefined constructor" << endl;
}
// 深拷贝构造函数
MAN::person::person(const person& pn):name(pn.name),age(pn.age),male(pn.male)
{
/*
this->name = pn.name;
this->age = pn.age;
this->male = pn.male;
*/
pInt = new int(*pn.pInt); // 深拷贝
cout << "copy constructor" << endl;
}
MAN::person::~person()
{
// 默认析构函数是空的
delete this->pInt; // 释放单个内存
// delete[] this->pInt; // 释放数组内存
cout << "userdefined destructor" << endl;
}