原创博文,转载请标明出处--周学伟http://www.cnblogs.com/zxouxuewei/

1.什么是拷贝构造函数:

拷贝构造函数,又称复制构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。

2.调用拷贝构造函数的情形:

(1)一个对象以值传递的方式传入函数体

#include <iostream>
#include <cstring> using namespace std; class test
{
public:
    test();
    test(const test& ctest);
    ~test();
private:
    char Name;
}; test::test()
{
    cout <<"Construction "<<endl;
} test::test(const test& ctest)
{
    cout <<"Copy Construction "<<endl;
} test::~test()
{
    cout <<"Destruction "<<endl;
} void function(test param_test)
{
    cout <<"into function"<<endl;
} int main(int argc, char const *argv[])
{
    test Atest;
    function(Atest);     return 0;
}

运行结果


(2)一个对象以值传递的方式从函数返回

#include <iostream>
#include <cstring> using namespace std; class test
{
public:
    test();
    test(const test& ctest);
    ~test();
private:
    char Name;
}; test::test()
{
    cout <<"Construction "<<endl;
} test::test(const test& ctest)
{
    cout <<"Copy Construction "<<endl;
} test::~test()
{
    cout <<"Destruction "<<endl;
} test function(test param_test)
{
    cout <<"into function"<<endl;
    return param_test;
} int main(int argc, char const *argv[])
{
    test Atest;
    function(Atest);     return 0;
}

运行结果:


(3)一个对象需要通过另外一个对象进行初始化。

#include <iostream>
#include <cstring> using namespace std; class test
{
public:
    test();
    test(const test& ctest);
    ~test();
private:
    char Name;
}; test::test()
{
    cout <<"Construction "<<endl;
} test::test(const test& ctest)
{
    cout <<"Copy Construction "<<endl;
} test::~test()
{
    cout <<"Destruction "<<endl;
} int main(int argc, char const *argv[])
{
    test Atest;
    test Btest = Atest;     return 0;
}

运行结果:

 3.浅拷贝和深拷贝:

 在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。

---》深拷贝:(查看在堆上分配的内存资源进行深拷贝)

#include <iostream>
#include <cstring> using namespace std; class test
{
public:
    test(char* name);
    test(const test& ctest);
    ~test();
    void Printf();
private:
    char* PName;
}; test::test(char* name)//普通构造函数
{
    if(name != NULL)
    {
        cout <<"Construction "<<name<<endl;
        int len = strlen(name)+1;
        PName = new char[len];         memset(PName, 0, len);
        strcpy(PName,name);
    }
    else
    {
        PName = NULL;
    }
    cout <<"&PName = "<<static_cast<void*>(PName)<<endl;
} test::test(const test& ctest)//深拷贝构造函数
{
    if(ctest.PName != NULL)
    {
        cout <<"Copy Construction "<<endl;
        int len = strlen(ctest.PName)+1;
        PName = new char[len];
        memset(PName, 0, len);
        strcpy(PName,ctest.PName);
    }
    else
    {
        PName = NULL;
    }
    cout <<"&PName = "<<static_cast<void*>(PName)<<endl;
} test::~test()
{
    cout <<"Destruction "<<endl;
    if(PName != NULL)
    {
        Printf();
        delete []PName;
        PName = NULL;
    }
} void test::Printf()
{
    cout <<"&PName = "<<static_cast<void*>(PName)<<endl;
} int main(int argc, char const *argv[])
{
    test Atest((char*)"zhouxuewei");     test Btest = Atest;//复制对象的副本,此刻调用拷贝构造函数     return 0;
}

运行结果:

 ----》浅拷贝(使用编译器默认的拷贝构造函数)

#include <iostream>
#include <cstring> using namespace std; class test
{
public:
test(char* name);
//test(const test& ctest);
~test();
void Printf();
private:
char* PName;
}; test::test(char* name)//普通构造函数
{
if(name != NULL)
{
cout <<"Construction "<<name<<endl;
int len = strlen(name)+;
PName = new char[len]; memset(PName, , len);
strcpy(PName,name);
}
else
{
PName = NULL;
}
cout <<"&PName = "<<static_cast<void*>(PName)<<endl;
}
/*
test::test(const test& ctest)//深拷贝构造函数
{
if(ctest.PName != NULL)
    {
        cout <<"Copy Construction "<<endl;
        int len = strlen(ctest.PName)+1;
        PName = new char[len];
        memset(PName, 0, len);
        strcpy(PName,ctest.PName);
    }
    else
    {
        PName = NULL;
    }
cout <<"&PName = "<<static_cast<void*>(PName)<<endl;
}*/ test::~test()
{
cout <<"Destruction "<<endl;
if(PName != NULL)
{
Printf();
delete []PName;
PName = NULL;
}
} void test::Printf()
{
cout <<"&PName = "<<static_cast<void*>(PName)<<endl;
} int main(int argc, char const *argv[])
{
test Atest((char*)"zhouxuewei"); test Btest = Atest;//复制对象的副本,此刻调用拷贝构造函数 return ;
}

运行结果:结果可以看出,拷贝的对象使用的堆内存地址相同,析构时free两次,产生错误:

4.赋值运算符

#include <iostream>
#include <cstring> using namespace std; class test
{
public:
    test(char* name,int value);
    test(const test& ctest);
    ~test();
    test& operator=(const test& otest);
    void Printf();
private:
    char* PName;
    int value;
    static unsigned char count;
}; unsigned char test::count = 0; test::test(char* name,int val)//普通构造函数
{
    if(name != NULL)
    {
        int len = strlen(name)+1;
        PName = new char[len];         memset(PName, 0, len);
        strcpy(PName,name);
    }
    else
    {
        PName = NULL;
    }
    value = val;
    cout <<"&PName = "<<static_cast<void*>(PName)<<", name = "<<PName<<", value = "<<value<<endl;
    count++;
} test::test(const test& ctest)//深拷贝构造函数
{
    if(ctest.PName != NULL)
    {
        cout <<"Copy Construction "<<endl;
        int len = strlen(ctest.PName)+1;
        PName = new char[len];
        memset(PName, 0, len);
        strcpy(PName,ctest.PName);
    }
    else
    {
        PName = NULL;
    }
    cout <<"&PName = "<<static_cast<void*>(PName)<<", name = "<<PName<<endl;
    count++;
} test::~test()
{
    cout <<"Destruction "<<", count = "<<static_cast<int>(count)<<endl;
    if(PName != NULL)
    {
        Printf();
        delete []PName;
        PName = NULL;
    }
    count--;
} test& test::operator=(const test& otest)
{
    cout <<"into operator "<<endl;
   if(&otest == this)  //自赋值检查
   {
       return *this;
   }
   if(PName != NULL)
   {
        delete []PName;  //原有资源的释放
        PName = NULL;
   }
   if(otest.PName != NULL)
   {
        int len = strlen(otest.PName)+1;//开始赋值
        PName = new char[len];
        memset(PName, 0, len);
        strcpy(PName, otest.PName);
   }
   else
        PName = NULL;
    value = otest.value;
    return *this;
} void test::Printf()
{
    cout <<"&PName = "<<static_cast<void*>(PName)<<",name = "<<PName<<", value = "<<value<<endl;
} int main(int argc, char const *argv[])
{
    test Atest((char*)"zhouxuewei",111);
    test Btest((char*)"wanghuixi",222);     Atest = Btest;     return 0;
}

运行结果:

总结:

1》何时需要定义拷贝构造函数:

  a.类数据成员有指针。

  b.类数据成员管理资源(如打开文件)

2》如果一个类需要析构函数来释放资源,则他需要一个拷贝构造函数。

3》如果想禁止一个类的拷贝构造,则需要将拷贝构造函数声明为private。

c++浅拷贝和深拷贝---14的更多相关文章

  1. 编写高质量代码改善C#程序的157个建议——建议14: 正确实现浅拷贝和深拷贝

    建议14: 正确实现浅拷贝和深拷贝 为对象创建副本的技术称为拷贝(也叫克隆).我们将拷贝分为浅拷贝和深拷贝. 浅拷贝 将对象中的所有字段复制到新的对象(副本)中.其中,值类型字段的值被复制到副本中后, ...

  2. 编写高质量代码改善C#程序的157个建议[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]

    前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议13.为类型输出格式化字符串 建议14.正确实现浅拷贝和深 ...

  3. 浅析JavaScript解析赋值、浅拷贝和深拷贝的区别

    文章首发于sau交流学习社区 一.赋值(Copy) 赋值是将某一数值或对象赋给某个变量的过程,分为: 1.基本数据类型:赋值,赋值之后两个变量互不影响 2.引用数据类型:赋**址**,两个变量具有相同 ...

  4. js对象浅拷贝和深拷贝详解

    js对象浅拷贝和深拷贝详解 作者:i10630226 字体:[增加 减小] 类型:转载 时间:2016-09-05我要评论 这篇文章主要为大家详细介绍了JavaScript对象的浅拷贝和深拷贝代码,具 ...

  5. C#程序编写高质量代码改善的157个建议【13-15】[为类型输出格式化字符串、实现浅拷贝和深拷贝、用dynamic来优化反射]

    前言 本文已更新至http://www.cnblogs.com/aehyok/p/3624579.html .本文主要学习记录以下内容: 建议13.为类型输出格式化字符串 建议14.正确实现浅拷贝和深 ...

  6. java中的浅拷贝和深拷贝

    复制 将一个对象的引用复制给另一个对象,一共有三种方式.第一种方式是直接赋值,第二种方式是浅复制,第三种方式是深复制. 1.直接赋值 在Java中,A a1 = a2,这实际上复制的是引用,也就是说 ...

  7. [转] js对象浅拷贝和深拷贝详解

    本文为大家分享了JavaScript对象的浅拷贝和深拷贝代码,供大家参考,具体内容如下 1.浅拷贝 拷贝就是把父对像的属性,全部拷贝给子对象. 下面这个函数,就是在做拷贝: var Chinese = ...

  8. 【转】Python中的赋值、浅拷贝、深拷贝介绍

    这篇文章主要介绍了Python中的赋值.浅拷贝.深拷贝介绍,Python中也分为简单赋值.浅拷贝.深拷贝这几种"拷贝"方式,需要的朋友可以参考下   和很多语言一样,Python中 ...

  9. 渐析java的浅拷贝和深拷贝

          首先来看看浅拷贝和深拷贝的定义:       浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式被称为浅拷贝.       深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所 ...

随机推荐

  1. font-face 跨域解决

    nginx 里设置@font-face 跨域 server { ... # Fix @font-face cross-domain restriction in Firefox location ~* ...

  2. UMEditor(Ueditor mini)修改图片上传路径

    UMEditor(Ueditor mini)修改图片上传路径 imageUp.ashx string pathbase = "/UpLoad/images/"; //保存文件夹在网 ...

  3. SQL Server创建远程链接服务器

    --使用sp_addlinkedserver增加链接 EXEC sys.sp_addlinkedserver @server='127.0.0.1', --被访问的服务器别名(习惯上直接使用目标服务器 ...

  4. 使用postman测试文件上传

    调试API神奇----postman 请求方法:POST Body-->form-data-->key(选择file) QQ技术交流群:282575808 ---------------- ...

  5. php curl_multi系列函数实现多线程抓取网页

    最近几天在做一个多搜索引擎关键字排名查询工具,用于及时方便的了解关键词在各大搜索引擎的排名. 在抓取360搜索的时候,发现360搜索每页只支持显示10个搜索结果,如果想获取100个搜索结果数据,就得搜 ...

  6. 你应该知道的CSS2.0中最常用的18条技巧

    一.使用css缩写 使用缩写可以帮助减少你CSS文件的大小,更加容易阅读.  具体内容请浏览:CSS常用缩写语法 二.明确定义单位,除非值为0. 忘记定义尺寸的单位是CSS新手普遍的错误.在HTML中 ...

  7. 【转】MYSQL数据库四种索引类型的简单使用--MYSQL组合索引“最左前缀”原则

    MYSQL数据库索引类型包括普通索引,唯一索引,主键索引与组合索引,这里对这些索引的做一些简单描述: (1)普通索引 这是最基本的MySQL数据库索引,它没有任何限制.它有以下几种创建方式: 创建索引 ...

  8. 关于go语言的环境配置 SDK+path+工作目录

    第一步: 安装Golang的SDK http://golang.org,下载最新的安装包,之后双击安装即可. 安装完成之后,打开终端,输入go.或者go version(查看安装版本)出现如下信息即表 ...

  9. 关于GO语言遇到illegal UTF-8 encoding 随手记录

    在使用汉字的时候会报错 解决方案 editpad++ 修改编码为UTF-8 保存就可以了~bingo

  10. FileOutPutStream in 创新实训 自然语言交流系统

    FileOutPutStream在c盘等一级目录下是可以创建文件的,如: new FileOutputStream("c:\\kk.txt");但是在c\\test等就创建不了,F ...