深浅拷贝
1.什么是浅拷贝?里面存在什么问题?
解:浅拷贝也称位拷贝,编译器只是将指针的值拷贝过来。然后会导致多个对象共用同一块空存,当一个对象将这块内存释放掉之后,另一些对象不知道该空间已经还给了系统,以为还有效,所以在对这段内存进行操作的时候,发生了访问违规。
下面来具体看一下有关浅拷贝的代码:
#include <iostream>
#include <string>
using namespace std;
class String
{
public:
//构造函数
String(char *str = "")
:_str(str)
{}
//修改后的构造函数
String(char *str = "")//构造函数
{
if (NULL == str)
{
_str = new char[1];//为了和delete[]配合使用
*_str = '\0';
}
else
{
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
}
//拷贝构造函数(浅拷贝)程序会崩溃(同一空间被释放多次)
String(const *String &s)
:_str(s._str)
{}
//拷贝构造函数(深拷贝)重新开辟空间
String(const *String &s)
:_str(new char[strlen(s._str)+1])
{
strcpy(_str, s._str);
}
//赋值运算符重载
//方法一 (先释放,再开辟空间,但空间开辟可能会失败,不仅没有将s1赋值给s2,还易破坏原来的s2)
String& operator = (const String& s)
{
if(this != &s)
{
delete[] _str;
_str = new char[strlen(s._str)+1];
strcpy(_str, s._str);
}
return *this;
}
//方法二(先开辟空间,赋值,再释放,不用担心破坏原来的对象)
String& operator = (const String& s)
{
if(this != NULL)
{
char* temp = new char[strlen(s._str)+1];
strcpy(temp, s.-str);
delete[] _str;
_str = temp;
}
return *this;
}
//析构函数
~String()
{
if(NULL != _str)
{
delete[] _str;
}
}
private:
//数据成员
char *_str;
};
注:
当类里有指针对象是,拷贝构造函数和赋值运算符重载只进行值拷贝(浅拷贝),两个对象指向同一块内存,对象销毁时该空间被释放了两次,所以程序崩溃。
2.用深拷贝怎么解决上面浅拷贝出现的问题?
解:构造s2时拷贝一块跟s1指向数据块一样大的数据块,并将值拷贝下来,这样s1和s2指向各自的数据块,析构时释放各自的数据块。
下面同样来看一下深拷贝的代码:
#include <iostream>
using namespace std;
class String
{
public:
//构造函数
String(char* str = "")
:_str(new char[strlen(str) + 1])
{
strcpy(_str, str);
}
//拷贝构造函数
String(const String& s)
{
this->_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}
//赋值运算符重载
String& operator=(const String& s)
{
if (this != &s)
{
delete[] _str;
_str = new char[strlen(s._str) + 1];
strcpy(_str, s._str);
}
return *this;
}
//析构函数
~String()
{
if (_str)
{
delete[] _str;
}
}
private:
char* _str;
};
写时拷贝—Copy On Write
引用计数,像new开辟空间那样对开辟四个字节保存计数数字
注:有几块空间就有几个计数
3.什么是引用计数?
解:在引用计数中,每一个对象负责维护对象所有引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源。
首先来看一下引用计数的代码(ps:代码可能不太成熟,还在研究完善ing)
#include <iostream>
using namespace std;
class String
{
public:
String(const char* str = "")
{
if(NULL = str)
{
_str = new char[1];
*str = '\0';
}
else
{
_str = new char[strlen(str)+1];
strcpy(_str, str);
}
_count = 1;
}
String(String &s)
:_str(s._str)
,_count(++(s._count))
{
strcpy(_str, s._str);
}
~String()
{
//_count是每个对象的,减去一个共享一块空间的其中一个对象的成员
//并不会改变其他对象的成员变量_count的值
if((--_count == 0)&&(NULL != _str))
{
cout<<this<<endl;
delete[] _str;
}
}
private:
char *_str;
};
然后看一下写时拷贝的代码:
String.h
#include <iostream>
#include <assert.h>
using namespace std;
class String
{
public:
//构造函数
String(const char* str = "")
{
if(NULL == str)
{
char* temp =new char[1+3];
_pStr = temp + 4;
_GetRefer() = 1;
*_pStr = '\0';
}
else
{
char* temp = new char[strlen(str)+1+3];
_pStr = temp + 3;
strcpy(_pStr, str);
_GetRefer() = 1;
}
}
//拷贝构造函数
String(const String& s)
:_pStr(s._pStr)
{
_GetRefer()++;
}
//析构函数
~String()
{
Release();
}
String& operator=(const String& s);
char& operator[](size_t index);
const char& operator[](size_t index)const;
int& String::_GetRefer();
void Release();
friend ostream& operator<<(ostream& _cout, const String& s);
private:
char *_str;
}
String.cpp
//运算符重载
String& String:: operator = (const String &s)
{
if(_pStr != s._pStr)
{
if(--_GetRefer() == 0)
{
Release();
_pStr = s._pStr;
_GetRefer()++;
}
else
{
_pStr = s._pStr;
_GetRefer()++;
}
}
return *this;
}
//获取引用计数
int& String ::_GetRefer()
{
return *((int*)_pStr-1);
}
//释放空间
void String ::Release()
{
if(--_GetRefer() == 0)
{
_pStr -= 3;
delete[] _pStr;
_pStr == NULL;
}
}
//成对出现
char* String ::operator [](size_t index)
{
if(_GetRefer() > 1)
{
_GetRefer()--;
char* temp = new char[strlen(_pStr)+1+3];
temp += 3;
strcpy(temp, _pStr);
-pStr = temp;
_GetRefer() = 1; //(新开的空间)
}
return _pStr[index];
}
const char* String ::operator [](size_t index)const
{
return _pStr[index];
}
void Test1()
{
const String s1("hello");
String s2(s1);
//String s3;
//s3 = s2;
cout << s1[4] << endl;
}
int main()
{
Test1();
return 0;
}
注:有几块空间就有几个计数
今天就先学习到这里,还有好多要完善,会继续改进!!!(ps:欢迎大家指正错误,共同进步)
纸上得来终觉浅,绝知此事要躬行。