摘要
学过或者了解过C++都知道,,,在C++中面向对象实现一个类 、、、的时候必须要考虑的就是
【深浅拷贝】的问题 ??
所以我写了这篇文章来简要的讲述一下C++中深浅拷贝的问题以及由此引发的 另一个 概念【写实拷贝】
概念的解析
所谓的深浅拷贝就是C++中;;;;;定义一个类的时候;;;
其拷贝构造函数、、、还有赋值运算符的重载 的不同的实现方法;;;
当一个类的成员变量中带有的是 指针;;;
当使用这个类对象拷贝生成一个对象的的时候、、、如果直接赋值的话,,,,,类析构的时候就会出现问题;;;
所以在这种时候 我们就需要来考虑深拷贝的问题;;;;
关于深拷贝的问题、、、大致主要有两种的方法: 方法1、 直接重新申请一段相同大小的空间、、、并赋值成相同的值 ;;;
这就是直接的深拷贝;
方法2、 在类的成员变量中加上一个 【引用计数 】用来表示当前有多少个对象在公用一段空间
类析构时、(要是引用计数为1 直接释放这段空间 )(否则的话就将这个对象的成员变量的指针置为NULL)
这种方法 就是 带有引用计数的浅拷贝的 实现深拷贝
【灰常的重要】
但是上述的方法2 虽然实现了 深拷贝 、、、但是还遗留了一个很大的问题;;;
假设有 4 个string类的对象 a b c d;公用的是 同一段的空间
现在要对 对象a 的内容进行修改、、、、 但是要是修改了 a的 内容 b c d的内容也会被修改(因为它们用的是同一段空间)
所以也就出现了 【写实拷贝】这个 概念》》》》
当遇到上述这种情况下、、、、谁 改变 就对谁进行拷贝 ------这就是 写实拷贝
上述的两种方法 Windows系统下的编程环境使用的是 方法1 Linux系统下是用的是 方法2
通常情况下、、、、、我们都是使用的是 string来演示实现 深拷贝、、、与写实拷贝的
代码的实现
下面我来实现这段代码的实现#pragma once
#include <assert.h>
//说道string类的两个主要考点就是
//深拷贝
//浅拷贝 写实拷贝 ()
//深拷贝
class String
{
public:
String(const char * str = "")
{
//要是传进来的字符串为 NULL
if(str == NULL)
{
//我们需要创建一个内容为 /0的字符串
_str =new char [1];
*_str = '/0';
}
else
{
//否则,,,开辟一个长度为 str长度+1;
_str = new char [strlen(str)+1];
strcpy(_str,str);
}
}
//深拷贝的拷贝构造函数
//String(const String & str)
//:_str(new char[strlen(str._str)+1])
//{
////先为字符串开辟一个空间
////将内部的数据拷贝
//strcpy(_str,str._str);
//}
//简介版 的拷贝构造函数
String (const String & str)
:_str(NULL)//现将当前的对象的字符串的指针赋为空 ,,,必须要写
{
//使用的是 str内部的字符串的指针,,来构造一个新的对象 str1;
String str1(str._str);
//将当前的对象的_str 与 重新生成的新对象的_str 交换
swap(_str,str1._str);
}
//深拷贝的赋值运算符的重载
//String & operator = (const String & str)
//{
//if(this!= &str)
//{
////开辟一个空间
//char * tmp =new char[strlen(str._str)+1];
////将原来的空间释放
//delete[]_str;
////将内容拷贝到所开辟的位置
//strcpy(tmp,str._str);
////将开辟的空间给当前的类
//_str = tmp;
//}
//return *this;
//}
//简介版 的赋值运算符的重载
String & operator =(const String & str)
{
//防止自己给自己赋值
if(this != & str)
{
String str1(str);//使用拷贝构造函数 ,,,来生成一个新的对象
//先将当前对象的_str释放 ,,,,并且赋值成 NULL
delete[] _str;
_str = NULL;
swap(_str,str1._str);
}
return *this;
}
~String()
{
if(_str != NULL)
{
delete [] _str;
}
_str =NULL;
}
//重载【】
char & operator[](size_t n)
{
assert(n < strlen(_str) );
return *(_str+n);
}
protected:
char *_str;
};
//引用计数的浅拷贝 写实拷贝
class StringS
{
public:
StringS(const char * str = "")
{
//要是传进来的字符串为 NULL
if(str == NULL)
{
//我们需要创建一个内容为 /0的字符串
_str =new char [1];
*_str = '/0';
}
else
{
//否则,,,开辟一个长度为 str长度+1;
_str = new char [strlen(str)+1];
strcpy(_str,str);
}
_count = new int(1);
}
//拷贝构造函数
StringS(const StringS & str)
:_str (str._str)
,_count(str._count)
{
(*_count)++;
}
//带有引用计数的赋值运算符的重载
StringS & operator=( const StringS & str)
{
if(_str != str._str)
{
if(*_count == 1)
{
delete[] _str;
_str = str._str;
}
else
{
(*_count)--;
_str = str._str;
}
_count = str._count;
(*_count) ++;
}
return *this;
}
~StringS()
{
if(*_count == 1)
{
delete[] _str;
delete _count;
}
else
{
(*_count) --;
}
_str = NULL;
_count =NULL;
}
//写实拷贝
//当某个对象要进行 写的话 ,,,就要对其 单独进行拷贝
char & operator[](size_t n)
{
assert(n < strlen(_str) );
if(*_count != 1)//如果这个数 多个 对象使用的话
{
char * tmp = new char[strlen(_str)+1];
strcpy(tmp,_str);
_str = tmp;
(*_count)--;
_count = new int(1);
}
return *(_str+n);
}
protected:
char * _str;
int * _count ;//表示的是 引用计数
};
void testString()
{
String st(NULL);
String st1;
String st2("we are happy");
String st3(st2);
st1 = st3;
}
void testStringS()
{
StringS st(NULL);
StringS st1;
StringS st2("we are happy");
StringS st3(st2);
st1 = st3;
st2[2];
}