• 技术文章 >后端开发 >C#.Net教程

    C++---浅拷贝、深拷贝、写时拷贝讲解(附代码)

    php是最好的语言php是最好的语言2018-08-04 13:44:51原创1336

    对于普通的类型来说,拷贝没什么大不了的。

    int a = 0;int b = a;

    不会出现任何问题。

    而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量。

    浅拷贝

    首先来说说我们常遇到的浅拷贝的情况。

    #include <stdio.h> class student
    {public:
        student()      // 构造函数,p指向堆中分配的一空间
        {
            _name = new char(100);        printf("默认构造函数\n");
        }
        ~student()     // 析构函数,释放动态分配的空间
        {        if (_name != NULL)
            {            delete _name;
                _name = NULL;            printf("析构函数\n");
            }
        }private:    char * _name;     // 一指针成员};int main()
    {
        student a;
        student b(a);   // 复制对象
        return 0;
    }

    这段代码乍看之下没什么毛病,通过类的默认构造函数将 a 复制给 b ,但是一旦运行就会程序崩溃
    经过我的刻苦学习与钻研,终于发现其中的问题所在。
    由于我的类没有拷贝构造函数,所以student b(a)会调用,编译器自动生成的一个默认拷贝构造函数,该构造函数完成对象之间的位拷贝。位拷贝又称浅拷贝
    浅拷贝

    如何避免浅拷贝害人呢?
    养成自定义拷贝构造函数的习惯,当显式定义了拷贝构造函数后,编译器就会调用拷贝构造函数了,为了不出现程序崩溃,请使用自定义拷贝构造函数,当然我们自己如果把代码写成了浅拷贝的形式,那也不是不可能的事。

    深拷贝

    // 使用自定制拷贝构造函数,完成深拷贝!!!class A
    {public:
        A()      // 构造函数,p指向堆中分配的一空间
        {
            m_pdata = new char(100);        printf("默认构造函数\n");
        }
    
        A(const A& r)    // 拷贝构造函数
        {
            m_pdata = new char(100);    // 为新对象重新动态分配空间
            memcpy(m_pdata, r.m_pdata, strlen(r.m_pdata));        printf("copy构造函数\n");
        }
    
        ~A()     // 析构函数,释放动态分配的空间
        {        if (m_pdata != NULL)
            {            delete m_pdata;            printf("析构函数\n");
            }
        }private:    char *m_pdata;     // 一指针成员};int main()
    {
        A a;
        A b(a);   // 复制对象
        return 0;
    }

    在拷贝构造函数中,为 b 对象 new 了一个新的空间,这样 a 和 b 指向的是不同的空间,只是内容一致,但是互不影响。
    重复的去开辟空间和释放空间效率是很低的,聪明的地球人决定使用写时拷贝。

    写时拷贝

    写时拷贝:引入一个计数器,每片不同内容的空间上都再由一个计数器组成,在构造第一个类指向时,计数器初始化为1,之后每次有新的类也指向同一片空间时,计数器加 1 ;在析构时判断该片空间对应计数器是否为1,为1则执行清理工作,大于1则计数器减 1 。如果有需要进行增删等操作时,再拷贝空间完成,有利于提高效率。

    class String
    {
    public:
        String(const char* str = "")
            :_str(new char[strlen(str) + 1 + 4])//+1表示字符串后面要放一个'\0',+4表示多开辟一个空间存放引用计数
        {        _str += 4;//_str指向数据存放区
            strcpy(_str, str);        _GetCount() = 1;
        }
        String(const String& s)
            :_str(s._str)
        {        _GetCount()++;
        }
        String& operator=(String& s)
        {        if (this != &s)
            {            if (--_GetCount() == 0)
                {
                    delete[](_str - 4);
                }
                ++s._GetCount();            _str = s._str;
            }        return *this;
        }
        ~String()
        {        if (--_GetCount() == 0)
            {
                delete[](_str - 4); // 注意:由于计数器存放在了_str首地址-4的地址上,所以在析构时一定要注意全部释放,避免内存泄漏。
            }
        }
    public:    int& _GetCount()
        {        return *((int*)_str - 1);
        }
    private:    char* _str;
    
    };

    相关文章:

    C#浅拷贝和深拷贝实例解析

    Python中的赋值、浅拷贝、深拷贝介绍

    以上就是C++---浅拷贝、深拷贝、写时拷贝讲解(附代码)的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:深拷贝 浅拷贝
    上一篇:C#使用Newtonsoft的Json.NET进行对象的序列化与反序列化 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • 【c#教程】C# 属性(Property)• 应用绝对路径与相对路径• ASP.NET使用Ajax如何返回Json对象的方法具体介绍• 解决asp.net中“从客户端中检测到有潜在危险的Request.Form值”的错误• asp.net 图片验证码的HtmlHelper
    1/1

    PHP中文网