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: