首页 > 代码库 > C++的拷贝构造函数

C++的拷贝构造函数

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>

首先在创建第一个员工的时候程序没有报错。在创建第二个对象的时候就报错了,因为第二步是把第一个员工赋值的,很明显第二步只是简单的调用了拷贝构造函数,这种情况我们必须知道编译器写的拷贝构造函数做什么事情了,才能知道为什么报错了.所以先写出员工类的拷贝构造函数看看先........













C++的拷贝构造函数