拷贝构造的功能
拷贝构造函数 可以把曾经实例化好的对象的数据 拷贝给新创建的数据,可见说拷贝构造函数在功能上是构造函数的另一种形式。都是让对象初始化。
写法:
拷贝构造函数在语法层是构造函数的重载,函数名就是类名,无返回值,参数是该类类型对象的引用(为了保护被引用对象数据不被破坏,会在类型前面加上const)
cpp
Date(const Date& d) //以日期类为例,定义拷贝构造的写法
cpp
Date d1;
Date d2(d1); //以日期类为例,实例化对象时的写法
拷贝构造函数的参数为什么是引用类型
下面是定义的日期类的拷贝构造函数,但参数并不是引用类型
cpp
Date(const Date d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
因为是传值,实例化对象时需要调用拷贝构造函数把数据拷贝过来,但拷贝数据函数的参数又需要掉用拷贝构造函数 ,如此就死循环了,拷贝构造函数会被无限调用。如图所示这种情况编译器会强制报错。
系统自动生成的拷贝构造函数
若未显式定义,编译器会生成默认的拷贝构造函数 。默认的拷贝构造函数对象按内存存储 按字节序 完成拷贝,这种拷贝叫做浅拷贝 ,或者值拷贝。
下面代码中定义了一个时间类 Time ,又定义了一个日期类 Date , 时间类在日期类里实例化了对象_t,_t就是日期类的自定义类型,但并没有定义日期类的拷贝构造函数。那么系统自动生成的拷贝构造函数会怎么处理自定义类型_t和内置类型的年月日呢?
cpp
class Time { //时间类
public:
Time()
{
_hour = 1;
_minute = 1;
_second = 1;
}
Time(const Time& t) //自定义时间类的拷贝构造函数
{
_hour = t._hour;
_minute = t._minute;
_second = t._second;
}
private:
int _hour;
int _minute;
int _second;
};
class Date //日期类,但没有自定义拷贝构造函数
{
private:
// 基本类型(内置类型)
int _year;
int _month;
int _day;
// 自定义类型
Time _t; //时间类实例化对象 _t
};
int main(){
Date d1;
Date d2(d1); //拷贝对象d1的数据
// 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
// 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构造函数
return 0;
}
只需明晰一点即可,系统自动生成的默认拷贝构造对内置类型进行浅拷贝,对自定义类型,会调用自定义类型的拷贝构造函数,若自定义类型并未定义拷贝构造函数,系统便会自动生成。
下图是代码执行示意图
下图是逻辑示意图
拷贝构造的深拷贝与浅拷贝
概念
浅拷贝:
又称值拷贝,是按字节序拷贝的,通俗的讲别的数据长啥样就拷贝啥样。
浅拷贝的细节:下面代码中还是定义了一个日期类,但增加了一个int*的变量它指向一块空间。这是让默认构造函数进行浅拷贝会发生什么呢?
cpp
class Date //日期类,并没有定义拷贝构造函数
{
public:
Date()//构造函数
{
_year = 1;
_month = 1;
_day = 1;
_a = (int*)malloc(sizeof(int) * 7); //开辟空间并把空间的值初始化
for (int i = 0; i < 7; i++)
{
_a[i] = 0;
}
}
private:
int _year;
int _month;
int _day;
int* _a; //指向一块空间
};
int main()
{
Date d;
Date d1(d); //拷贝d的值
}
结果:d对象和d1对象的数据会一模一样,如下图
我们发现d对象的值和d1对象的值一模一样,并且d对象中的_a和d1对象中的_a都指向同一块空间。如下图
上述就值浅拷贝的坏处,d1对象的_a并没有申请资源而是直接指向了d对象_a的空间,如果想让d1
_a也申请空间就需要深拷贝,想要深拷贝就需要自己定义一个拷贝构造函数,该拷贝构造函数如何申请空间要根据不同的场景具体实现。
深拷贝
下面的代码是对上面代码的改造,
cpp
class Date
{
public:
Date() //构造函数
{
_year = 1;
_month = 1;
_day = 1;
_a = (int*)malloc(sizeof(int) * 7);
for (int i = 0; i < 7; i++)
{
_a[i] = 0;
}
}
Date(const Date& d) //拷贝构造函数
{
_year = d._year;
_month = d._month;
_day = d._day;
_a = (int*)malloc(sizeof(int) * (sizeof(d._a) / sizeof(d._a[0]))); //开辟空间
for (int i = 0; i < (sizeof(d._a) / sizeof(d._a[0])); i++) //赋值
{
_a[i] = d._a[i];
}
}
private:
int _year;
int _month;
int _day;
int* _a;
};
int main()
{
Date d;
Date d1(d);
}
d1对象的_a会独自开空间并且拷贝d对象的数据,如下图这样就完成了拷贝构造的深拷贝了。
小结
拷贝构造是构造函数的重载 。参数是该类类型的引用 (一般会加上const),不加引用在逻辑是会让拷贝函数无限调用,此时编译器会报错。如果未显示定义拷贝构造函数,编译器会生成默认的拷贝构造函数**。默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝** ,或者值拷贝。浅拷贝又称值拷贝,是按字节序拷贝的,通俗的讲别的数据长啥样就拷贝啥样。而深拷贝会申请资源(空间)再赋值。
另外我还为大家准备了几篇不错的博客
构造函数: http://t.csdnimg.cn/MdHiA
希尔排序: http://t.csdnimg.cn/uxSBG
堆排序的时间复杂度: http://t.csdnimg.cn/OJLzI