最近在重温C++ Effective系列,在more effective C++中Item28~30涉及一个string类的实现,为了深入地理解,故由简单到复杂实现一个简易的string类,这一篇里,不考虑书中的3个条款,在后续的博客里,逐步将条款的内容加入进来,实现一些相对复杂的特性;
string类声明:
class String
{
friend std::ostream& operator<<(std::ostream& os, const String&);
friend std::istream& operator>>(std::istream& is, String&);
friend bool operator < (const String& left_, const String& right_);
friend bool operator > (const String& left_, const String& right_);
friend bool operator != (const String& left_, const String& right_);
friend bool operator == (const String& left_, const String& right_);
friend String operator + (const String& left_, const String& right_);
public:
String(const char* str_ = nullptr);
String(const String& str_);
String& operator=(const String& str_);
String& operator=(const char* str_);
~String();
public:
inline size_t size() const;
inline const char* c_str() const;
inline void swap(String& str_);
public:
String& operator += (const String&);
String& operator += (const char*);
const char& operator[](size_t index_) const;
char& operator[](size_t index_);
private:
char* _ptr;
size_t _size;
};
string类实现:
String::String(const char* str_/* = nullptr */)
{
if (str_ == nullptr)
{
_ptr = new char[1];
_ptr[0] = '\0';
_size = 0;
}
else
{
size_t len = strlen(str_) + 1;
_ptr = new char[len];
strcpy(_ptr, str_);
_size = len - 1;
}
}
String::String(const String& str_)
: String(str_._ptr)
//调用上个构造函数完成构造,降低代码重复
{
/*_ptr = new char[str_._size + 1];
strcpy(_ptr, str_._ptr);
_size = str_._size;*/
}
String& String::operator=(const String& str_)
{
if (this == &str_)
{
return *this;
}
return operator=(str_._ptr);
}
String& String::operator=(const char* str_)
{
//异常安全
/*String tmp(str_);
swap(tmp);*/
swap(String(str_));
return *this;
}
String::~String()
{
if (_ptr != nullptr)
{
delete[] _ptr;
_ptr = nullptr;
_size = 0;
}
}
size_t String::size() const
{
return _size;
}
const char* String::c_str() const
{
return _ptr;
}
void String::swap(String& str_)
{
std::swap(_ptr, str_._ptr);
std::swap(_size, str_._size);
}
String& String::operator+=(const String& str_)
{
return operator+=(str_.c_str());
}
String& String::operator+=(const char* str_)
{
//strcpy(dst, src)函数,需保证src不为nullptr
if (str_ == nullptr || *str_ == '\0')
{
return *this;
}
//异常安全
size_t len = _size + strlen(str_) + 1;
char* tmp = new char[len];
strcpy(tmp, _ptr);
strcat(tmp, str_);
std::swap(_ptr, tmp);
_size = len - 1;
delete[] tmp;
return *this;
}
const char& String::operator[](size_t index_) const
{
if (index_ >= _size)
{
throw std::out_of_range("String : out of index!");
}
return _ptr[index_];
}
//调用const版本的operator[],降低代码重复
//反过来,不建议用const调用non-const,这
//样可能会导致潜在的bug
char& String::operator[](size_t index_)
{
return const_cast<char&>(
(static_cast<const String&>(*this))[index_]);
}
std::ostream& operator<<(std::ostream& os, const String& str_)
{
os << str_._ptr;
return os;
}
std::istream& operator>>(std::istream& is, String& str_)
{
//预分配1个字节
char* str = (char*)malloc(sizeof(char) * 1);
size_t capacity = 1; //记录当前分配空间的容量
char* buff = str;
//预处理流的所有空格与回车
//*buff = getchar() 或者is.get(*buff)均可
while ((*buff = getchar()) == ' ' || *buff == '\n');
//while (is.get(*buff) && (*buff == ' ' || *buff == '\n'));
int index = 1;
while (1)
{
//回车跳出
if (*buff == '\n')
{
*buff = '\0';
break;
}
//空格跳出
else if (*buff == ' ')
{
*buff = '\0';
break;
}
//空间不足
else if (index == capacity)
{
//容量加倍,并分配对应容量的空间
capacity *= 2;
str = (char*)realloc(str, capacity);
}
//为了避免realloc返回首地址改变,不使用++buff,而是str+偏移量
buff = (str + index);
*buff = getchar();
//is.get(*buff); //每次读取一个字符
index++;
}
//输入完成
str_ = String(str);
free(str);
str = nullptr;
buff = nullptr;
return is;
}
bool operator < (const String& left_, const String& right_)
{
bool less = true;
int index = 1;
for (; index < left_.size() && index < right_.size(); ++index)
{
if (left_[index] != right_[index])
{
less = left_[index] < right_[index];
break;
}
}
if (index == left_.size() || index == right_.size())
{
less = left_.size() < right_.size();
}
return less;
}
bool operator > (const String& left_, const String& right_)
{
return (left_ != right_) && !(left_ < right_);
}
bool operator == (const String& left_, const String& right_)
{
if (left_.size() != right_.size())
{
return false;
}
bool equal = true;
for (int i = 0; i < left_.size(); ++i)
{
if (left_[i] != right_[i])
{
equal = false;
break;
}
}
return equal;
}
bool operator != (const String& left_, const String& right_)
{
return !(left_ == right_);
}
String operator + (const String& left_, const String& right_)
{
String tmp(left_);
tmp += right_;
return tmp;
}
需要注意的地方:
1.赋值运算符重载里,需要判断赋值对象是否是自身,不过,在我的实现中,通过swap函数保证异常安全,即使没有以上的判断,也能正常运行;
2.friend std::istream& operator>>(std::istream& is, String&)函数,实现不能是简单的如下代码:
friend std::istream& operator>>(std::istream& is, String& str_)
{
is>>str_._ptr;
return is;
}
因为,参数str_可能是一个通过默认构造函数构造的对象,其 ptr指针指向的空间仅有一个字符(’\0’),is>>str . _ptr向这个空间写入数据,会导致溢出,在VS2013平台,虽然该函数能”正常”运行,但在该String对象被析构时,会因delete[] _ptr而出错;
3.为了最大可能消除代码冗余,函数执行效率会下降,例如operator>就需要调用operator<和operator=,这里是在代码的冗余度,维护难度和执行效率间的协调间偏向前两者;
4.friend std::istream& operator>>(std::istream& is, String&)的实现参考了如下链接:
https://zhidao.baidu.com/question/711251936107526885.html
5.operator[]函数,通过non-const版本调用const版本,消除代码冗余,这是在某本书上学习到的方法(具体书名忘记了);
由于在C++上实践不多,加上算法能力弱,如果有实现的代码有bug或者设计问题,希望可以评论中提出来,非常感谢^-^