1、string类浅拷贝造成的问题
/*
*此程序会崩溃
*由于s2对s1浅拷贝,导致多次释放同一块空间
* */
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<assert.h>
using namespace std;
class String
{
public:
String(const char * str = "")
{
if(nullptr == str)
{
assert(false);
return;
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
~String()
{
if(_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char * _str;
};
int main()
{
String s1("hello");
String s2(s1);
return 0;
}
- String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造,导致s1、s2共用同一块内存空间。
- 在释放时,s2将其_str所指空间释放掉,s2对象成功销毁,s1中_str成为野指针,销毁出错,同一块空间被释放多次而引起程序崩溃,这种拷贝方式称为浅拷贝。
2、浅拷贝
- 浅拷贝,也称位拷贝,编译器只是将对象中的值拷贝过来
- 如果对象中管理资源,就会导致多个对象共享同一份资源
- 当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以当继续对资源进项操作时,就会发生访问违规
- 引入深拷贝解决浅拷贝问题
3、深拷贝
如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
String(const String& s)
:_pStr(new char[strlen(s._pStr) + 1])
{
strcpy(_pStr, s._pStr);
}
深拷贝:给每个对象独立分配资源,保证多对象之间不会因资源共享造成多次释放引起的程序崩溃
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<assert.h>
using namespace std;
class String
{
public:
String(const char * str = "")
{
if(nullptr == str)
{
assert(false);
return;
}
_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& operator=(const String& str)
{
if(this != &str)
{
delete[] _str;
_str = new char[strlen(str._str) + 1];
strcpy(_str, str._str);
}
return *this;
}
~String()
{
if(_str)
{
delete[] _str;
_str = nullptr;
}
}
void display()
{
cout<<_str<<endl;
}
private:
char * _str;
};
int main()
{
String s1("hello");
String s2(s1);
String s3 = s2;
s1.display();
s2.display();
s3.display();
return 0;
}
4、写时拷贝
- 写时拷贝:是在浅拷贝的基础之上增加了引用计数的方式来实现的。
- 引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放,否则就不能释放,因为还有其他对象在使用该资源。
5、string类的模拟实现
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<assert.h>
using namespace std;
class String
{
public:
typedef char * iterator;
public:
String(const char * str = "")
{
if(str == nullptr)
{
str = "";
}
_size = strlen(str);
_capacity = 10;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
String(const String& s)
:_size(s._size),
_capacity(s._capacity),
_str(new char[_capacity + 1])
{
strcpy(_str, s._str);
}
String& operator=(const String& s)
{
if(this != &s)
{
delete[] _str;
_size = s._size;
_capacity = s._capacity;
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}
return *this;
}
~String()
{
if(_str)
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
}
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
void PushBack(char c)
{
if(_size == _capacity)
{
Reserve(_capacity * 2);
}
_str[_size] = c;
_str[++_size] = '\0';
}
String& operator+=(char c)
{
PushBack(c);
return *this;
}
void Append(const char * str)
{
for(int i = 0; i < strlen(str); ++i)
{
PushBack(str[i]);
}
}
String& operator+=(const char * str)
{
Append(str);
return *this;
}
void Clear()
{
strcpy(_str, "");
_size = strlen(_str);
_capacity = 10;
_str = new char[_capacity + 1];
}
void Swap(String& s)
{
swap(_str, s._str);
swap(_size, s._size);
swap(_capacity, s._capacity);
}
const char * C_str() const
{
return _str;
}
size_t Size() const
{
return _size;
}
size_t Capacity() const
{
return _capacity;
}
bool Empty() const
{
if(_size == 0)
{
return 1;
}
return 0;
}
void Resize(size_t newSize, char c = '\0')
{
if(newSize > _size)
{
if(newSize > _capacity)
{
Reserve(newSize);
}
memset(_str + _size, c, newSize - _size);
}
_size = newSize;
_str[newSize] = '\0';
}
void Reserve(size_t newCapacity)
{
if(newCapacity > _capacity)
{
char * tmp = new char[newCapacity + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = newCapacity;
}
}
char& operator[](size_t index)
{
assert(index < _size);
return _str[index];
}
const char& operator[](size_t index) const
{
assert(index < _size);
return _str[index];
}
bool operator<(const String& s)
{
if((*this) == s)
{
return false;
}
for(int i = 0; i < _size; ++i)
{
if(_str[i] < s._str[i])
{
return true;
}
else if(_str[i] > s._str[i])
{
return false;
}
}
if(_size < s._size)
{
return true;
}
}
bool operator<=(const String& s)
{
if(((*this) < s) || ((*this) == s))
{
return true;
}
return false;
}
bool operator>(const String& s)
{
if((*this) == s)
{
return false;
}
for(int i = 0; i < _size; ++i)
{
if(_str[i] > s._str[i])
{
return true;
}
else if(_str[i] < s._str[i])
{
return false;
}
}
if(_size < s._size)
{
return false;
}
}
bool operator>=(const String& s)
{
if(((*this) > s) || ((*this) == s))
{
return true;
}
return false;
}
bool operator==(const String& s)
{
if(s._size != _size)
{
return false;
}
for(int i = 0; i < _size; ++i)
{
if(s[i] != _str[i])
{
return false;
}
}
return true;
}
bool operator!=(const String& s)
{
if(s._size != _size)
{
return true;
}
for(int i = 0; i < _size; ++i)
{
if(s[i] != _str[i])
{
return true;
}
}
return false;
/*
if((*this) == s)
{
return false;
}
return true;
*/
}
size_t Find(char c, size_t pos = 0) const
{
for(int i = pos; i < _size; ++i)
{
if(c == _str[i])
{
return i;
}
}
return -1;
}
size_t Find(const char * s, size_t pos = 0) const
{
for(int i = pos; i < _size; ++i)
{
int j = 0;
for(j = 0; j < strlen(s); ++j)
{
if(s[j] != _str[i + j])
{
break;
}
}
if(j == strlen(s))
{
return i;
}
}
return -1;
}
String& Insert(size_t pos, char c)
{
if(_size == _capacity)
{
Reserve(_capacity * 2);
}
_str[_size + 1] = '\0';
for(int i = _size - 1; i >= pos; --i)
{
_str[i + 1] = _str[i];
}
_str[pos] = c;
_size++;
return *this;
}
String& Insert(size_t pos, const char * str)
{
for(int i = 0; i < strlen(str); ++i)
{
Insert(pos + i, str[i]);
}
return *this;
}
String& Erase(size_t pos, size_t len)
{
int index = pos;
for(int i = pos + len; i < _size; ++i)
{
_str[pos++] = _str[i];
}
_str[pos] = '\0';
_size -= len;
return *this;
}
private:
friend ostream& operator<<(ostream& _cout, const String& s);
private:
char * _str;
size_t _capacity;
size_t _size;
};
ostream& operator<<(ostream& _cout, const String& s)
{
_cout<<s._str<<" "<<s._size<<" "<<s._capacity;
return _cout;
}
int main()
{
String s1("hello");
cout<<s1<<endl;
cout<<s1.Size()<<endl;
cout<<s1.Capacity()<<endl;
cout<<s1.Empty()<<endl;
s1.PushBack('c');
cout<<s1<<endl;
s1 += 'c';
cout<<s1<<endl;
s1.Append("linux");
cout<<s1<<endl;
s1 += "code";
cout<<s1<<endl;
s1.Clear();
cout<<s1<<endl;
s1.Append("linux");
cout<<s1<<endl;
String s2;
cout<<s2<<endl;
s2.Append("linuxcode");
cout<<s2<<endl;
s1.Swap(s2);
cout<<s1<<endl;
cout<<s1.C_str()<<endl;
cout<<s2<<endl;
s2.Resize(20);
cout<<s2<<endl;
cout<<s2[2]<<endl;
String s3("hello");
String s4("linux");
cout<<"s3==s4:"<<(s3==s4)<<endl;
cout<<"s3!=s4:"<<(s3!=s4)<<endl;
cout<<"s3<s4:"<<(s3<s4)<<endl;
cout<<"s3<=s4:"<<(s3<=s4)<<endl;
cout<<s4<<endl;
cout<<s4.Find("nux")<<endl;
s4.Insert(2, " hello code ");
cout<<s4<<endl;
s4.Erase(2, 12);
cout<<s4<<endl;
return 0;
}
Makefile文件:
.PHONY:all
all:String
String:String.cpp
g++-4.8 -std=c++11 -o $@ $^
.PHONY:clean
clean:
rm -rf String