string类、string类模拟实现、深浅拷贝、写实拷贝

1. 为什么学习string类?

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户自己管理,稍不留神可能还会越界访问。

2. 标准库中的string

2.1 string

1. string是表示字符串的字符串类

2. 该类的接口与常规容器的接口基本相同,再添加了一些专门用来操作string的常规操作。

3. string在底层实际是:basic_string模板类的别名,typedef basic_string string;

4. 不能操作多字节或者变长字符的序列。

使用string类时,必须包含头文件以及using namespace std;

2.2 string类的常用接口说明

详情请见本人博客收藏。或地址https://blog.csdn.net/tx_812214/article/details/89266591

 

3. string类的模拟实现

3.1 经典的string类问题

class String
{
public:
String(const char* str = "")
 {
// 构造string类对象时,如果传递nullptr指针,认为程序非法,此处断言下
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;
};
// 测试
void TestString()
{
String s1("hello bit!!!");
String s2(s1);
}

说明:上述String类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。最终导致的问题是,s1s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。

3.2 浅拷贝

浅拷贝:也称位拷贝,编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为还有效,所以 当继续对资源进项操作时,就会发生发生了访问违规。要解决浅拷贝问题,C++中引入了深拷贝。

3.3 深拷贝

如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。

3.3.1 传统版写法的String

class String
{
public:
String(const char* str = "")
 { 
// 构造string类对象时,如果传递nullptr指针,认为程序非法,此处断言下
if(nullptr == str)
 {
assert(false);
return;
 }
_str = new char[strlen(str) + 1];
strcpy(_str, str);
 }
String(const String& s)
 : _str(new char[strlen(s._str)+1])
 {
strcpy(_str, s._str);
 }
String& operator=(const String& s)
 {
if(this != &s)
 {
char* pStr = new char[strlen(s._str) + 1];
strcpy(pStr, s._str);
delete[] _str;
_str = pStr;
 }
return *this;
 }
~String()
 {
if(_str)
 {
delete[] _str;
_str = nullptr;
 }
 }
private:
char* _str;
};

3.3.2 现代版写法的String

class String
{
public:
String(const char* str = "")
 {
if(nullptr == str)
str = "";
_str = new char[strlen(str) + 1];
strcpy(_str, str);
 }
String(const String& s)
 : _str(nullptr)
 {
String strTmp(s._str);
swap(_str, strTmp);
 }
// 对比下和上面的赋值那个实现比较好?
String& operator=(String s)
 {
swap(_str, s._str); 
return *this;
 }
/*
String& operator=(const String& s)
{
if(this != &s)
{
String strTmp(s);
swap(_str, strTmp._str);
}
return *this;
}
*/
~String()
 {
if(_str)
 {
delete[] _str;
_str = nullptr;
 }
 }
private:
char* _str;
};

3.3 写时拷贝

引用计数:用来记录资源使用者的个数。在构造时,将资源的计数给成1,每增加一个对象使用该资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,如果计数为1,说明该对象时资源的最后一个使用者,将该资源释放;否则就不能释放,因为还有其他对象在使用该资源。

3.4 string类的模拟实现

namespace bit
{
class String
 {
public:
typedef char* Iterator;
public:
String(const char* str = "")
 {
// 构造string类对象时,如果传递nullptr指针,认为程序非法,此处断言下
if (nullptr == str)
 {
assert(false);
return;
 }
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity+1];
strcpy(_str, str);
 }
String(const String& s)
 : _str(new char[s._capacity + 1])
 , _size(s._size)
 , _capacity(s._capacity)
{
strcpy(_str, s._str);
 }
String& operator=(const String& s)
 {
if (this != &s)
 {
char* pStr = new char[s._capacity + 1];
strcpy(pStr, s._str);
delete[] _str;
_str = pStr;
_size = s._size;
_capacity = s._capacity;
 }
return *this;
 }
~String()
 {
if (_str)
 {
delete[] _str;
_str = nullptr;
 }
 }
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';
 }
void Append(size_t n, char c)
{
for (size_t i = 0; i < n; ++i)
PushBack(c);
 }
String& operator+=(char c)
 {
PushBack(c);
return *this;
 }
void Clear()
 {
_size = 0;
_str[_size] = '\0';
 }
void Swap(String& s)
 {
swap(_str, s._str);
swap(_size, s._size);
swap(_capacity, s._capacity);
 }
const char* C_Str()const
 {
return _str;
 }
/////////////////////////////////////////////////////////////////
// capacity
size_t Size()const
 {
return _size;
 }
size_t Capacity()const
 {
return _capacity;
 }
bool Empty()const
 {
return 0 == _size;
 }
void Resize(size_t newSize, char c = char())
 {
if (newSize > _size)
{
// 如果newSize大于底层空间大小,则需要重新开辟空间
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* str = new char[newCapacity + 1];
strcpy(str, _str);
// 释放原来旧空间,然后使用新空间
delete[] _str;
_str = str;
_capacity = newCapacity;
 }
 }
////////////////////////////////////////////////////////////////////
// access
char& operator[](size_t index)
 {
assert(index < _size);
return _str[index];
 }
const char& operator[](size_t index)const
 {
assert(index < _size);
return _str[index];
 }
size_t Find (char c, size_t pos = 0) const;
// 返回子串s在string中第一次出现的位置
size_t Find (const char* s, size_t pos = 0) const;
// 截取string从pos位置开始的n个字符
String SubStr(size_t pos, size_t n);
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
String& Insert(size_t pos, char c);
String& Insert(size_t pos, const char* str);
// 删除pos位置上的元素,并返回该元素的下一个位置
String& Erase(size_t pos, size_t len);
private:
friend ostream& operator<<(ostream& _cout, const bit::String& s);
private:
char* _str;
size_t _capacity;
size_t _size;
 };
}
ostream& bit::operator<<(ostream& _cout, const bit::String& s) {
cout << s._str;
return _cout; }
///////////////////////////////////////////////////////////////////////////////////////////
///////对自定义的string类进行测试
void TestBitString1()
{
bit::String s1;
bit::String s2("hello bit");
bit::String s3(s2);
s1 = s3;
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl; }
void TestBitString2()
{
bit::String s1("hello");
s1.PushBack(' ');
s1.PushBack('b');
s1.Append(1, 'i');
s1 += 't';
cout << s1 << endl;
cout << s1.Size() << endl;
cout << s1.Capacity() << endl;
// 利用迭代器打印string中的元素
auto it = s1.Begin();
while (it != s1.End())
 {
cout << *it++;
 }
cout << endl;
bit::String s2("hello world!!!");
s1.Swap(s2);
cout << s1 << endl;
cout << s2 << endl; }
void TestBitString3()
{
bit::String s("hello");
cout << s << endl;
cout << s.Size() << endl;
cout << s.Capacity() << endl; s.ReSize(10, 'a');
cout << s << endl;
cout << s.Size() << endl;
cout << s.Capacity() << endl; s.ReSize(20);
cout << s << endl;
cout << s.Size() << endl;
cout << s.Capacity() << endl; s.ReSize(5);
cout << s << endl;
cout << s.Size() << endl;
cout << s.Capacity() << endl; s.Reserve(50);
cout << s << endl;
cout << s.Size() << endl;
cout << s.Capacity() << endl; }

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(c++,基础知识)