STL学习——浅谈string类的模拟实现

        C++ 的一个常见面试题是让你实现一个 String 类,限于时间,不可能要求具备 std::string 的功能,但至少要求能正确管理资源。具体来说:

1.能像 int 类型那样定义变量,并且支持赋值、复制。
2.能用作函数的参数类型及返回类型。
3.能用作标准库容器的元素类型,即 vector/list/deque 的 value_type。

        本文给出我认为适合面试的答案,强调正确性及易实现(白板上写也不会错),不强调效率。某种意义上可以说是以时间(运行快慢)换空间(代码简洁)。

        首先选择数据成员,最简单的 String 只有一个 char* 成员变量。好处是容易实现,坏处是某些操作的复杂度较高(例如 size() 会是线性时间)。为了面试时写代码不出错,本文设计的 String 只有一个 char* data_成员。而且规定 invariant 如下:一个 valid 的 string 对象的 data_ 保证不为 NULL,data_ 以 ‘\0’ 结尾,以方便配合 C 语言的 str*() 系列函数。

        其次决定支持哪些操作,构造、析构、拷贝构造、赋值这几样是肯定要有的(以前合称 big three,现在叫 copy control)。如果钻得深一点,C++11的移动构造和移动赋值也可以有。为了突出重点,本文就不考虑 operator[] 之类的重载了。

这样代码基本上就定型了:

#include <utility>
#include <string.h>
 
class String
{
 public:
  String()
    : data_(new char[1])
  {
    *data_ = '\0';
  }
 
  String(const char* str)
    : data_(new char[strlen(str) + 1])
  {
    strcpy(data_, str);
  }
 
  String(const String& rhs)
    : data_(new char[rhs.size() + 1])
  {
    strcpy(data_, rhs.c_str());
  }
  /* Delegate constructor in C++11
  String(const String& rhs)
    : String(rhs.data_)
  {
  }
  */
 
  ~String()
  {
    delete[] data_;
  }
 
  /* Traditional:
  String& operator=(const String& rhs)
  {
    String tmp(rhs);
    swap(tmp);
    return *this;
  }
  */
  String& operator=(String rhs) // yes, pass-by-value
  {
    swap(rhs);
    return *this;
  }
 
  // C++ 11
  String(String&& rhs)
    : data_(rhs.data_)
  {
    rhs.data_ = nullptr;
  }
 
  String& operator=(String&& rhs)
  {
    swap(rhs);
    return *this;
  }
 
  // Accessors
 
  size_t size() const
  {
    return strlen(data_);
  }
 
  const char* c_str() const
  {
    return data_;
  }
 
  void swap(String& rhs)
  {
    std::swap(data_, rhs.data_);
  }
 
 private:
  char* data_;
};

注意代码的几个要点:

1.只在构造函数里调用 new char[],只在析构函数里调用 delete[]。
2.赋值操作符采用了《C++编程规范》推荐的现代写法。
3.每个函数都只有一两行代码,没有条件判断。
4.析构函数不必检查 data_ 是否为 NULL。
5.构造函数 String(const char* str) 没有检查 str 的合法性,这是一个永无止境的
争论话题。这里在初始化列表里就用到了 str,因此在函数体内用 assert() 是无意义的。

这恐怕是最简洁的 String 实现了。

接下来我们模拟实现较完备的string类

成员变量:

private:
 char* _str;
 size_t _capacity;
 size_t _size;
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
 Iterator Begin()
 {
 return _str;
 }
 Iterator End()
 {
 return _str + _size;
 }

 /////////////////////////////////////////////////////////////////
 // modify

 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];
 }

//输出运算符的重载
 friend ostream& operator<<(ostream& _cout, const bit::String& s);

private:
 char* _str;
 size_t _capacity;
 size_t _size;
 };


ostream& operator<<(ostream& _cout, String& s)
{
 cout << s._str;
 return _cout;
}

这个功能比较完备的版本啦,需要提醒的是:

1.迭代器iterator实质上是一个char*的封装

typedef  char*  iteraotor;

2.resize()和reserve()的区分:
当申请的空间大于原空间的时候,两者都会改变string对象的大小;不同的是resize()会将多余的空间初始化为’\0’或者指定字符,reserve()只是申请了指定空间而已;
当申请的空间小于原空间的时候,reserve()不会产生什么影响,但是resize()却会将原空间大小改为申请大小,并且实际字符串内容也会改变。

//test1();
	string s("abcdefg");
	cout << s.capacity() << endl;
	cout << s.size() << endl;
	cout << s << endl;

	s.resize(3);
	cout << s.capacity() << endl;
	cout << s.size() << endl;
	cout << s << endl;

输出结果 :
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/tonglin12138/article/details/92840698