1? 类会提供默认的拷贝构造函数
–默认的拷贝构造函数会完成所有成员的逐个复制
2? 拷贝构造的调用时机:
–函数值传递时
–函数返回时
–用同类型的对象初始时
3? 何时需要自定义拷贝构造函数?
–类中有指针(或引用 )成员时
–希望自定义对象的拷贝过程时
4? 使用匿名对象简化编程
// // main.cpp // 拷贝构造函数 // // Created by 06 on 15/1/26. // Copyright (c) 2015年 黄永锐. All rights reserved. // #pragma 创建对象的时候,有可能是通过构造函数,也有可能是通过拷贝构造函数创建的。 #include <iostream> using namespace std; //日期类 class Date{ int year,month,day; public: Date(){//无参构造 cout << "日期类的构造函数" << endl; } ~Date(){ cout << "日期的析构函数" << endl; } }; //主函数 int main(int argc, const char * argv[]) { //首先创建一个对象 然后通过它来赋值. 这里到底创建了几个对象?销毁了几个对象? Date d; Date d2 = d; return 0; }
运行结果如下:
日期类的构造函数 日期的析构函数 日期的析构函数 Program ended with exit code: 0
//结果显示创建了一个对象 销毁了两个 问题出在哪里? 其实是创建了两个对象,只是有一个对象没有经过构造函数
在Date日期类当中添加上拷贝构造函数如下:
<span style="font-size:18px;">//拷贝构造函数,如果我们自己没有写,编译器会帮我们写一个拷贝构造函数 //用同类型的对象创建对象的时候会用到拷贝构造函数,会调用d2的拷贝构造函数把d传进去 /* * Date(Date d3){ //注意这里啊,这里的参数如果是值传递,也就相当于(Date d3 = d)..这里又调用了d3的拷贝构造函数。。。。也就是说这里会导致死循环。所以在这一步要避免创建新对象,创建新对象就要调用拷贝 } * */ #pragma 拷贝构造函数里面的参数类型必须是引用 Date (Date& d3){//这里把那个对象本身传进来并没有创建新的对象 这样就不会调用拷贝了 //d3是d的引用(别名)...用同类型的对象创建对象,也可以说是用d来给d2初始化. //this在这里就代表d2 this->year = d3.year;//把传进来的对象的成员变量给新的对象的成员变量赋值 this->month = d3.month; this->day = d3.day; cout << "拷贝构造函数Date (Date& d3)" << endl; }</span>
再次运行结果如下可看出程序创建对象的过程:
<span style="font-size:18px;">日期类的构造函数 拷贝构造函数Date (Date& d3) 日期的析构函数 日期的析构函数 Program ended with exit code: 0</span>
构造函数创建了一个对象,拷贝构造函数也创建了一个对象.析构释放2个对象.....但是编译器给我们写的拷贝构造函数不是这个样的。如果我们把自己写的那个拷贝构造函数注释掉,把d用const来修饰然后赋值给d2。
const Date d; Date d2 = d;
当我们注释掉自己写的拷贝构造函数的时候,程序可以运行。但是当打开我们自己写的拷贝构造函数的时候,程序就出错了!
这是为什么呢????
因为在赋值的时候,调用d2的拷贝构造函数把d本身传进来了.那么在d2的拷贝构造函数里面d的成员变量能不能修改呢?
<span style="font-size:18px;">d3.year = 2013;</span>
在拷贝构造函数里面这个d的成员变量可以修改,那么这样岂不是和我在main函数里面的
<span style="font-size:18px;">const Date d;</span>
相违背了???
所以说我们的拷贝构造函数有可能会修改到那个const d对象的成员变量,所以就提示报错不让你调用d来给d2赋值
Date d2 = d;
一般这种情况那怎么办??
我们只要告诉编译器拷贝构造函数里面的参数是不能修改的即可。也就是用const来修饰
也就是编译器给我们写的拷贝构造函数大约如下:
#pragma 拷贝构造函数里面的参数类型必须是引用 Date (const Date& d3){//这里把那个对象本身传进来并没有创建新的对象 这样就不会调用拷贝了 //d3是d的引用(别名)...用同类型的对象创建对象,也可以说是用d来给d2初始化. //this在这里就代表d2 // d3.year = 2013; this->year = d3.year;//把传进来的对象的成员变量给新的对象的成员变量赋值 this->month = d3.month; this->day = d3.day; cout << "拷贝构造函数Date (Date& d3)" << endl; }
再次运行结果如下:
日期类的构造函数 拷贝构造函数Date (Date& d3) 日期的析构函数 日期的析构函数 Program ended with exit code: 0
那么问题又来了。我不写,编译器还会帮我写,我自己写有可能const还会忘记了。那为什么我们要写?
有没有什么情况必须自己写拷贝构造函数,如果不写就有问题????
就像上一篇文章说到析构函数的时候,如果一个类里面有一个指针是指向别的类的对象的时候,析构函数必须自己写。同时拷贝构造也必须自己写!
为什么?都是这个指针捣蛋的。现在在拷贝构造函数里面
<span style="font-size:18px;"> this->year = d3.year;//把传进来的对象的成员变量给新的对象的成员变量赋值 this->month = d3.month; this->day = d3.day;</span>
只是拿d的值给当前对象赋值。如果是指针的话,我拿d的这个指针给当前对象赋值,赋值的是个地址(浅拷贝,两个指针指向同一个对象)
现在再写一个员工类,里面有个员工的生日成员变量是指向日期类对象的指针
<span style="font-size:18px;">//员工类 class Employee{ string name; Date *birth;//这是一个指针 public: Employee(string name):name(name){ birth = new Date;//指向堆中的一块内存 cout << "员工类的构造函数" << endl; } ~Employee(){ delete birth;//清除指向堆的空间 cout << "员工类的析构函数" << endl; } };</span>
在main函数当中创建员工对象
<span style="font-size:18px;">//主函数 int main(int argc, const char * argv[]) { // Employee em("假如我是张三"); return 0; }</span>
运行结果:
<span style="font-size:18px;">日期类的构造函数 员工类的构造函数 日期的析构函数 员工类的析构函数 Program ended with exit code: 0</span>
两个构造,两个析构,没问题.然后我再在main函数里面根据第一个员工创建第二个员工
<span style="font-size:18px;"> Employee em("假如我是张三"); Employee em2 = em;</span>
程序奔溃了!!
<span style="font-size:18px;">日期类的构造函数 员工类的构造函数 日期的析构函数 员工类的析构函数 日期的析构函数 拷贝构造函数(2824,0x7fff7399c300) malloc: *** error for object 0x100104cb0: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug (lldb) </span>
首先在创建第一个员工的时候程序没有报错。在创建第二个对象的时候就报错了,因为第二步是把第一个员工赋值的,很明显第二步只是简单的调用了拷贝构造函数,这种情况我们必须知道编译器写的拷贝构造函数做什么事情了,才能知道为什么报错了.所以先写出员工类的拷贝构造函数看看先........