1. 浅拷贝
浅拷贝,是指原对象与拷贝对象共用一份实体,仅仅是对象名字不同而已(类似引用,即对原对象起别名),其中任何一个对象改变都会导致其他的对象也跟着它变。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
//g++ -o StringSimple StringSimple.cpp
class String
{
public:
String(const char* pStr = "")//构造函数
:_pStr(new char[strlen(pStr)+1])
{
if(0 == *pStr)//字符串为空
{
*_pStr = '\0';
}
else//字符串不为空
{
strcpy(_pStr,pStr);
}
}
String(const String& s)//拷贝构造函数
{
_pStr = s._pStr;
}
String& operator=(String& s)//赋值运算符重载
{
if(_pStr != s._pStr)//判断是不是自己给自己赋值
{
_pStr = s._pStr;
}
return *this;
}
~String()//析构函数
{
if(NULL == _pStr)
{
return;
}
else
{
delete []_pStr;
_pStr = NULL;
}
}
char* getStrPoint() {
return _pStr;
}
private:
char* _pStr;
};
int main()
{
String str1("abcdeaaaaa");
String str2(str1);
String str3;
str3 = str1;
//三个地址是一样的
printf(" str1: %lx\n", (long)str1.getStrPoint());
printf(" str2: %lx\n", (long)str2.getStrPoint());
printf(" str3: %lx\n", (long)str3.getStrPoint());
sleep(5);
return 0;
}
![](https://i-blog.csdnimg.cn/blog_migrate/ceb9e544cf0408bcbf947c1e63d7d3a8.png)
从上面的日志可以看出:内存被多次释放 。可以考虑通过引用计数的方式避免多次释放,只有计数器的值为0时,才可能真正释放内存。
2. 引用计数
通过引入计数器cnt,来解决浅拷贝多次释放同一块内存的问题。
#include <stdio.h>
#include <string.h>
class String
{
public:
//构造函数
String(const char* pStr = "")
:_pStr(new char[strlen(pStr) + 1])
,_cnt(new int(0))
{
if (0 == *pStr) {
*_pStr = '\0';
}
else //字符串 不为空
{
strcpy(_pStr, pStr);
}
(*_cnt)++;
printf("==构造函数==\n");
}
String(const String& s) //拷贝构造函数
{
_pStr = s._pStr;
_cnt = s._cnt;
(*_cnt)++;
printf("==拷贝构造函数==\n");
}
~String() //析构函数
{
if(NULL == _pStr)
{
return ;
}
else
{
if ((--(*_cnt)) == 0)
{
delete _cnt;
delete []_pStr;
_cnt = NULL;
_pStr = NULL;
}
}
printf("==析构函数==\n");
}
String& operator=(const String& s) //赋值运算符
{
//先释放旧数据
if (NULL != _pStr)
{
if ((--(*_cnt)) == 0)
{
delete _cnt;
delete []_pStr;
_cnt = NULL;
_pStr = NULL;
printf("==赋值函数,释放旧内存==\n");
}
}
_pStr = s._pStr;
_cnt = s._cnt;
(*_cnt)++;
printf("==赋值函数==\n");
return *this;
}
private:
char* _pStr;
int* _cnt;
};
int main()
{
String str1("abcdeabbbb");
String str2(str1);
String str3;
str3 = str1;
return 0;
}
3. 深拷贝
每次拷贝都是重新申请一块内存,再把内容拷贝过去,当数据比较大时,这种方式是比较耗内存的,而且也会影响性能。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
class String
{
public:
String(const char* pStr = "")//构造函数
:_pStr(new char[strlen(pStr)+1])
{
if(0 == *pStr)//字符串为空
{
*_pStr = '\0';
}
else//字符串不为空
{
strcpy(_pStr,pStr);
}
}
String(const String& s)//拷贝构造函数
:_pStr(new char[strlen(s._pStr)+1])
{
strcpy(_pStr,s._pStr);
}
String& operator=(const String& s)//赋值运算符重载
{
if(_pStr != s._pStr)//判断是不是自己给自己赋值
{
if (NULL != _pStr)
{
delete []_pStr;//释放当前对象
}
char* temp = new char[strlen(s._pStr)+1];//先开辟一段新空间
strcpy(temp,s._pStr);//将原对象的值赋给新空间
_pStr = temp;//将当前对象指向新开辟的空间
}
return *this;
}
~String()//析构函数
{
if(NULL == _pStr)
{
return;
}
else
{
delete []_pStr;
_pStr = NULL;
}
}
char* getStrPoint() {
return _pStr;
}
private:
char* _pStr;
};
int main()
{
String str1("abcdecccc");
String str2(str1);
String str3;
str3 = str1;
//三个地址是不一样的
printf(" str1: %lx\n", (long)str1.getStrPoint());
printf(" str2: %lx\n", (long)str2.getStrPoint());
printf(" str3: %lx\n", (long)str3.getStrPoint());
return 0;
}
从运行的结果看,三个对象的地址是不一样的。
总结:
(1)浅拷贝,是指原对象与拷贝对象共用一份实体,存在内存被多次释放的问题;
(2)引用计数,引用计数的方法可以解决浅拷贝多次释放内存的问题;
(3)深拷贝,每个对象都申请自己独立的内存,各个对象间互不影响,当数据比较大时,比较耗内存,也影响性能。
水平有限,难免存在疏漏,欢迎指出错误,谢谢!