c++知识点----深浅拷贝

更多C++知识点:C++目录索引


浅拷贝

  • 概念:

    浅拷贝也称位拷贝,是指源对象与拷贝对象共用一份实体,仅仅是引用的变量不同(名称不同)。对其中任何一个对象的改动都会影响另外一个对象。

  • 浅拷贝的问题

    1. 当多个对象共用同一块内存,某一对象使用完毕后会进行析构,其他对象再次使用这块空间,发生非法访问

    2. 多个对象,必定析构多次,但只有一块内存,导致非法析构

  • 注意

    当在一个自定义的类中,没有给出构造函数,重载赋值运算符的时候,一般编译器就会以浅拷贝的形式自动合成。

  • 浅拷贝代码

class String
{
public:
    String(const char* str="")
        :_str(new char[strlen(str)+1])
    {
        strcpy(_str,str);
    }
    String(const String& s)
        :_str(s._str)
    {   
    }
    String& operator=(const String& s)
    {
        if(this!= &s)
        {
            _str = s._str;
        }
        return *this;
    }
    ~String()
    {
        if(_str)
        {
            delete[] _str;
            _str = NULL;
        }
    }
private:
    char* _str;
};

void Test()
{
    String s1("hello world");
    String s2=s1;
}

画图解释一下这个代码:

这里写图片描述


深拷贝

深拷贝是指源对象与拷贝对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响

代码一:传统写法


class  String
{
public:
    //构造
    String::String(const char* str )
    {
        if (str == NULL)
        {
            _str = new char[1];
            _str = '\0';

        }
        else
        {
            _str = new char[strlen(str) + 1];
            strcpy(_str, str);

        }
    }
    //拷贝构造
     //String s2(s1) 
    String::String(const String& s)
            :_str(new char[strlen(s._str)+1])
    {
        if (this != &s)
        {
            strcpy(_str, s._str);

        }
    }


    // s1 = s2 
    String& String::operator=(String s)
    {
        //1.将S2的字符串拷贝下来
        //2.释放原字符串s1空间
        //3.将临时字符串给s1

        if (this != &s)
        {
            char* tmp = new char[strlen(s._str) + 1];
            strcpy(tmp, s._str);
            delete[] _str;
            _str = NULL;

            _str = tmp;
        }

        return *this;

    }

    String::~String()
    {
        if (_str != NULL)
        {
            delete[] _str;
            _str = NULL;
        }
    }

private:

    char* _str;
    size_t _size;
    size_t _capacity;
};

代码二:现代写法

画图解释一下现代写法中的交换指针

这里写图片描述


String::String(const char* str="")
        :_str(new char[strlen(str)+1])
{
        strcpy(_str, str);      
}

void String::Swap(String& s)
{
    swap(_str, s._str);

}
//拷贝构造
String::String(const String& s)
{
    if (this != &s)
    {
        String tmp(s._str);//构造临时对象,出了作用域自动析构
        Swap(tmp);//将指针进行交换
    }
}

//赋值运算符重载
String& String::operator=(String s)
{
    if (this != &s)
    {
        String tmp(s._str);
        Swap(tmp);
    }
    return *this;
}

写时拷贝+引用计数

引用计数:

在引用计数中,每一个对象负责维护对象所有引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源。

两种引用计数方式:

方式一:重新开辟四个字节专门做引用计数

方式二:引用计数和字符串使用一个内存块,方便管理

这里写图片描述

代码一:方式一的引用计数

//引用计数为另一块空间
引用计数为另一块空间
class String
{
public:
    void show()
    {
        cout << _str << endl;
    }


    //构造
    String(const char* str="")
        :_str(new char[strlen(str)+1])
        , _pCount(new size_t(1))
    {
        strcpy(_str, str);
    }

    //析构
    ~String()
    {
        if (--(*_pCount) == 0)
        {
            delete[] _str;
            delete _pCount;
            _str = NULL;
            _pCount = NULL;
        }
    }
    //拷贝构造
    String(const String& s)
        :_str(s._str)
        , _pCount(s._pCount)
    {
        ++(*_pCount);
    }

    //赋值运算符重载
    String& operator=(const String& s)
    {
        if (this != &s)
        {
            if (--(*_pCount) == 0)
            {
                delete[] _str;
                delete _pCount;
                _str = NULL;
                _pCount = NULL;
            }
            _str = s._str;
            _pCount = s._pCount;
            ++(*_pCount);
        }


        return *this;
    }

    //写时拷贝
    void CopyOnWrite()
    {
        if (*_pCount > 1)
        {
            char* newstr = new char[strlen(_str) + 1];
            strcpy(newstr, _str);
            _str = newstr;

            --(*_pCount);
            _pCount = new size_t(1);
        }
    }

    //可读可写
    char& operator[](size_t pos)
    {
        CopyOnWrite();
        return _str[pos];
    }
    //可读
    const char& operator[](size_t pos) const
    {
        return _str[pos];
    }



private:
    char* _str;
    size_t* _pCount;
};

代码二:方式二的写时拷贝

//引用计数在头上
class String
{
public:
    //构造
    String(char* str = "")
        :_str(new char[strlen(str)+5])
    {
        *((int*)(_str - 4)) = 1;
        strcpy(_str + 4, str);
    }

    //拷贝构造

        // s2(s1) 
    String(const String& s)
        :_str(s._str)
    {
        ++(*((int*)(_str - 4)));
    }


    //赋值运算符重载
    //s2 = s1 
    String& operator=(const String& s)
    {
        if (this != &s)
        {
            if (--(*((int*)(_str - 4))) == 0)
            {
                delete[](_str - 4);
            }

            _str = s._str;

            ++(*((int*)(_str - 4)));
        }
        return *this;
    }


    //析构
    ~String()
    {
        if (--(*((int*)(_str - 4))) == 0)
        {
            delete[](_str - 4);
        }
    }


    const char* c_str()
    {
        return _str;
    }


    void CopyOnWrite()
    {
        if ((*((int*)(_str - 4))) > 1)
        {
            char* newstr = new char[strlen(_str) + 5];
            newstr += 4;
            strcpy(newstr, _str);

            --(*((int*)(_str - 4)));
            _str = newstr;
            (*((int*)(_str - 4))) = 1;
        }
    }


    char& operator[](size_t pos)
    {
        CopyOnWrite();
        return _str[pos];
    }


    const char& operator[](size_t pos) const
    {
        return _str[pos];
    }



private:
    char* _str; // 引用计数在头上 
};

猜你喜欢

转载自blog.csdn.net/zhangye3017/article/details/79873179