【c++】string类的实现



#include<string.h>
#include<utility>
using namespace std;

class String
{
public:
	String();
	String(const char* str);
	String(const String& rhs);//复制构造函数
	String& operator=(const String& other);//赋值操作
	~String();
	const char* c_str() const;
	size_t size()const;//长度操作
	void swap(String& rhs);//交换操作

private:
	char* m_data;
};
//默认构造函数
String::String():m_data(new char[1]){  *m_data = '\0';}

//构造函数
String::String(const char* str):m_data(new char[strlen(str)+1])
{
	strcpy(m_data,str);
}
	
//复制构造函数
String::String(const String& rhs):m_data(new char[rhs.size()+1])
{
	strcpy(m_data,rhs.c_str());
}
//赋值操作
String& String::operator=(const String& other)
{
	String tmp(other);
	swap(tmp);
	return *this;
}
size_t String::size() const
{
	return strlen(m_data);
}

void String::swap(String& rhs)
{
	std::swap(m_data,rhs.m_data);
}

const char* String::c_str()const
{
	return m_data;
}



参考资料:

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

  1. 能像 int 类型那样定义变量,并且支持赋值、复制。
  2. 能用作函数的参数类型及返回类型。
  3. 能用作标准库容器的元素类型,即 vector/list/deque 的 value_type。(用作 std::map 的 key_type 是更进一步的要求,本文从略)。

换言之,你的 String 能让以下代码编译运行通过,并且没有内存方面的错误。

 
 
  1. void foo(String x) 
  2.   
  3. void bar(const String& x) 
  4.   
  5. String baz() 
  6.   String ret("world"); 
  7.   return ret; 
  8.   
  9. int main() 
  10.   String s0; 
  11.   String s1("hello"); 
  12.   String s2(s0); 
  13.   String s3 = s1; 
  14.   s2 = s1; 
  15.   
  16.   foo(s1); 
  17.   bar(s1); 
  18.   foo("temporary"); 
  19.   bar("temporary"); 
  20.   String s4 = baz(); 
  21.   
  22.   std::vector<String> svec; 
  23.   svec.push_back(s0); 
  24.   svec.push_back(s1); 
  25.   svec.push_back(baz()); 
  26.   svec.push_back("good job"); 

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

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

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

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

 
 
  1. #include <utility> 
  2. #include <string.h> 
  3.   
  4. class String 
  5.  public
  6.   String() 
  7.     : data_(new char[1]) 
  8.   { 
  9.     *data_ = '\0'
  10.   } 
  11.   
  12.   String(const char* str) 
  13.     : data_(new char[strlen(str) + 1]) 
  14.   { 
  15.     strcpy(data_, str); 
  16.   } 
  17.   
  18.   String(const String& rhs) 
  19.     : data_(new char[rhs.size() + 1]) 
  20.   { 
  21.     strcpy(data_, rhs.c_str()); 
  22.   } 
  23.   /* Delegate constructor in C++11 
  24.   String(const String& rhs) 
  25.     : String(rhs.data_) 
  26.   { 
  27.   } 
  28.   */ 
  29.   
  30.   ~String() 
  31.   { 
  32.     delete[] data_; 
  33.   } 
  34.   
  35.   /* Traditional: 
  36.   String& operator=(const String& rhs) 
  37.   { 
  38.     String tmp(rhs); 
  39.     swap(tmp); 
  40.     return *this; 
  41.   } 
  42.   */ 
  43.   String& operator=(String rhs) // yes, pass-by-value 
  44.   { 
  45.     swap(rhs); 
  46.     return *this
  47.   } 
  48.   
  49.   // C++ 11 
  50.   String(String&& rhs) 
  51.     : data_(rhs.data_) 
  52.   { 
  53.     rhs.data_ = nullptr; 
  54.   } 
  55.   
  56.   String& operator=(String&& rhs) 
  57.   { 
  58.     swap(rhs); 
  59.     return *this
  60.   } 
  61.   
  62.   // Accessors 
  63.   
  64.   size_t size() const 
  65.   { 
  66.     return strlen(data_); 
  67.   } 
  68.   
  69.   const char* c_str() const 
  70.   { 
  71.     return data_; 
  72.   } 
  73.   
  74.   void swap(String& rhs) 
  75.   { 
  76.     std::swap(data_, rhs.data_); 
  77.   } 
  78.   
  79.  private
  80.   char* data_; 
  81. }; 

注意代码的几个要点:

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

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

练习1:增加 operator==、operator<、operator[] 等操作符重载。

练习2:实现一个带 int size_; 成员的版本,以空间换时间。

练习3:受益于右值引用及移动语意,在 C++11 中对 String 实施直接插入排序的性能比C++98/03要高,试编程验证之。(g++的标准库也用到了此技术。)

陈皓注:同时,大家可以移步看看我的一篇老文《STL中String类的问题》



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值