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>

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

时间: 2024-10-11 18:19:00

C++的拷贝构造函数的相关文章

【转载】C++拷贝构造函数(深拷贝,浅拷贝)

对于普通类型的对象来说,它们之间的复制是很简单的,例如:int a=88;int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量.下面看一个类对象拷贝的简单例子. #include <iostream>using namespace std;class CExample {private:     int a;public:     CExample(int b)     { a=b;}     void Show ()     {        cout<

构造函数、拷贝构造函数、赋值操作符

对于这样一种类与类之间的关系,我们希望为其编写“深拷贝”.两个类的定义如下: class Point { int x; int y; }; class Polygon : public Shape { Point *points; }; 1. 构造函数 //构造函数 Polygon(const Point &p) : _point(new Point) { this->_point->x = p.x; this->_point->y = p.y; } 2. 拷贝构造函数 /

C++拷贝构造函数(深拷贝,浅拷贝)

http://www.cnblogs.com/BlueTzar/articles/1223313.html 对于普通类型的对象来说,它们之间的复制是很简单的,例如:int a=88;int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量.下面看一个类对象拷贝的简单例子. 1 #include <iostream> 2 using namespace std; 3 4 class CExample { 5 private: 6 int a; 7 public: 8

拷贝构造函数和const成员函数

实验原因 说明如何使用const描述保护类数据不会意外修改. 编译环境 vc6sp6 + win7x64 工程下载 copyConstruction_constMemberFunction.zip 使用非const成员函数,引起的拷贝构造函数报错 [cpp] view plain copy class CStudent { /// 常量定义 public: enum {NAME_SIZE_MAX = 64}; /// 构造, 拷贝构造, 析构函数 public: CStudent(); CStu

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员)、拷贝构造函数

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员).拷贝构造函数  从概念上将,可以认为构造函数分为两个阶段执行: 1)初始化阶段: 2)普通的计算阶段.计算阶段由构造函数函数体中的所有语句组成. 一.构造函数初始化列表 推荐在构造函数初始化列表中进行初始化 1.对象成员及其初始化 <span style="font-size:14px;">#include <iostream> using namespace std;

C++构造函数 &amp; 拷贝构造函数 &amp; 派生类的构造函数 &amp; 虚继承的构造函数

构造函数 ,是一种特殊的方法 .主要用来在创建对象时初始化对象, 即为对象成员变量赋初始值,总与new运算符一起使用在创建对象的语句中 .特别的一个类可以有多个构造函数 ,可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载.(摘自百度百科构造函数). 一.最基本的构造函数 1 class Base 2 { 3 public: 4 Base(int var) : m_Var(var) 5 { 6 } 7 private: 8 int m_Var; 9 }; 以上构造函数的执行过程:

拷贝构造函数和赋值操作符

假设有一个如下的MyClass类: class MyClass { public: //构造函数 //拷贝构造函数 MyClass(const MyClass& that) : int_data_(that.int_data_), dbl_data_(that.dbl_data_), str_data_(that.str_data_) { } //赋值操作符 MyClass& operator = (const MyClass& that) { if(this != that) {

C++中拷贝构造函数和赋值函数被调用的时机

一.拷贝构造函数被调用的时机: 1. 当用类的一个对象去初始化该类的另一个对象(或引用)时系统自动调用拷贝构造函数实现拷贝赋值. 2. 若函数的形参为类对象,调用函数时,实参赋值给形参,系统自动调用拷贝构造函数. 3. 当函数的返回值是类对象时,系统自动调用拷贝构造函数. 二.赋值函数被调用的时机: 当用一个对象赋值给另一个对象时 注意一.二中初始化和赋值的区别 C++中拷贝构造函数和赋值函数被调用的时机,布布扣,bubuko.com

拷贝构造函数[c++]

拷贝构造函数何时会被调用? 1. 对象以值传递的方式传入函数参数 2.对象以值传递的方式从函数返回 3.对象需要通过另外一个对象进行初始化 下面我们来看代码: //#include <iostream> //using namespace std; //template <typename T, int MAX> //T:队列的类型,char,int,double,包括自己的struct .MAX:循环队列的最大长度 //class Queue //{ //private: //