前提说明
- 该模拟只是实现了
string容器 的基础、常用接口; - 该模拟采用多文件编程,在
.h 头文件中实现类的定义,在.cpp 源文件实现接口函数的定义; - 为了避免与STL库中的
string 类起冲突,我们将该模拟string实现在一个自定义命名空间中; string 容器在底层是以动态顺序表实现的,因此其成员变量同顺序表结构一样;
"mystring.h"头文件 :string类的定义和类成员函数的声明
#pragma once
#include <iostream>
namespace mySpace
{
class string
{
friend std::ostream& operator<<(std::ostream& _cout, const mySpace::string& s);
friend std::istream& operator>>(std::istream& _cin, mySpace::string& s);
public:
typedef char* iterator;
string(const char* s = "");
string(const string& s);
string& operator=(const string& s);
~string();
iterator begin()const;
iterator end()const;
size_t size()const;
size_t capacity()const;
bool empty()const;
void resize(size_t n, char c = '\0');
void reserve(size_t n);
char& operator[](size_t index);
const char& operator[](size_t index)const;
void push_back(char c);
string& operator+=(char c);
string& operator+=(const char* str);
void append(const char* str);
void clear();
void swap(string& s);
const char* c_str()const;
size_t find(char c, size_t pos) const;
size_t find(const char* s, size_t pos = 0) const;
string& insert(size_t pos, char c);
string& insert(size_t pos, const char* str);
string& erase(size_t pos, size_t len = 1);
bool operator<(const string& s);
bool operator<=(const string& s);
bool operator>(const string& s);
bool operator>=(const string& s);
bool operator==(const string& s);
bool operator!=(const string& s);
private:
char* _str;
size_t _size;
size_t _capacity;
};
}
extern void Test_string_1();
特别注意构造接口模拟
1. 注意浅拷贝问题
这里先模拟实现一个简易的string类:
#include<iostream>
using namespace std;
class myString
{
public:
myString(const char* str = "")
{
if (nullptr == str)
{
str = " ";
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
~myString()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
测试1:通过该类实现一个对象拷贝,一定会报错: 原因分析: 测试2:通过该类对象进行赋值,同样会报错: 原因分析:
问题解决 对于类来说,一旦涉及堆内存的管理,用户一定要显示提供:构造函数 、拷贝构造函数 、赋值重载函数 、析构函数
2. 深拷贝解决
- 深拷贝实现
拷贝构造函数 、赋值重载函数 - 本质:让每一个对象拥有独立的资源;
- 下面是代码实现
普通代码:
#include<iostream>
#pragma warning(disable:4996)
using namespace std;
class myString
{
public:
myString(const char* str = "")
{
if (nullptr == str)
{
str = " ";
}
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
myString(const myString& s)
:_str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
myString& operator=(const myString& s)
{
if (this != &s)
{
char* temp = new char[strlen(s._str) + 1];
strcpy(temp, s._str);
delete[]_str;
_str = temp;
}
return *this;
}
~myString()
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
进阶代码:
- 上面的普通代码其实重复操作非常多,代码冗余;
- 通过
swap 函数,巧妙复用代码,提高代码简洁性;
先实现一个string类 的swap 成员函数:
void String_swap(myString& s)
{
std::swap(_str, s._str);
}
优化拷贝构造函数:
myString(const myString& s)
:_str(nullptr)
{
myString temp(s._str);
String_swap(temp);
}
优化赋值重载函数:
myString& operator=(const myString& s)
{
if (this != &s)
{
myString temp(s._str);
String_swap(temp);
}
return *this;
}
myString& operator=(myString s)
{
String_swap(s);
return *this;
}
【注】后面的string模拟类还是使用普通方法,方便理解
3. 写时拷贝解决
只涉及写时拷贝概念,具体实现暂时不谈
- 概念:写时拷贝就是若两对象相同,且不修改,那么共用一个数据内存,并延迟析构;
- 实现:在浅拷贝基础上,增加一个计数器(用来指示该资源被调用的个数);
- 优点:
注意:
- 如果共享的资源需要修改时,就需要给修改的对象创建一个新的资源,以避免影响其他共享原资源的对象内容;
- 写时拷贝就是当修改时才进行拷贝(创建空间);
- 并不推荐大量采用写时拷贝机制,因为涉及线程安全问题;
4. 不同平台的构造方法
验证思路:
- 实例化一个大小>15的string对象;
- 拷贝构造一个新的string对象;
- 打印两对象地址,观察是否一致:
若地址不同:采用深拷贝; 若地址相同:采用写时拷贝;
测试代码:
#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
void TestCopy()
{
string s1(20, 'a');
string s2(s1);
printf("&s1: %p\n", s1.c_str());
printf("&s2: %p\n", s2.c_str());
}
int main()
{
TestCopy();
return 0;
}
- VS的
R.J.版本STL 按照深拷贝方式实现string 类: - Linux的
SGI版本的STL 按照写时拷贝方式实现string 类:
模拟string的完整源码
string模拟实现-git
|