STL learning - Analog On the string class implementation

        A common interview questions C ++ that allows you to achieve a String class, limited time, can not be required to have std :: string functions, but require at least be able to manage resources properly. Specifically:

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

        In this paper, I think the answer for an interview, stressed the correctness and easy to implement (written on the whiteboard is also wrong), no emphasis on efficiency. In a sense it can be said it is time (running speed) for space (code simple).

        First select data members, only one of the easiest String char * member variable. Benefits are easy to implement, the downside is the high complexity of certain operations (e.g., size () would be a linear time). In order to write code without mistakes during the interview, this design is only one of String char * data_ members. And a predetermined invariant follows: data_ string object is not a valid guarantee NULL, data_ ending '\ 0', to facilitate mating str C language * () family of functions.

        Second, decide what actions, construction, destructor, copy constructor support, the assignment is sure to have these kind of (formerly collectively big three, now called copy control). If a bit deeper drilled, C ++ and move the mobile structure 11 may also have assignment. To focus, we do not consider operator [] like a heavy load.

Such code is basically stereotypes:

#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_;
};

Note that the code of a few key points:

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

This is probably the most concise String achieved.

Next we simulate achieve a more complete string class

Member variables:

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

This feature is more complete version of it, you need to be reminded:

1. iterator iterator is essentially a package char *

typedef  char*  iteraotor;

2.resize () and reserve () distinction:
when the space is larger than the original application space , both of which can change the size of an object string; except that a resize () will be extra space is initialized to '\ 0' or specified character, reserve () just applied for a designated space it;
when space applications is less than the original space of time , reserve () will not have any effect, but the resize () but will instead apply the original size of the space size, and actual string content will change.

//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;

Output:
Here Insert Picture Description

Guess you like

Origin blog.csdn.net/tonglin12138/article/details/92840698