zoukankan      html  css  js  c++  java
  • STRING类的正确写法

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

    能像 int 类型那样定义变量,并且支持赋值、复制。
    能用作函数的参数类型及返回类型。
    能用作标准库容器的元素类型,即 vector/list/deque 的 value_type。(用作 std::map 的 key_type 是更进一步的要求,本文从略)。
    换言之,你的 String 能让以下代码编译运行通过,并且没有内存方面的错误。

    1 void foo(String x)
    2 {
    3 }
    4 void bar(const String& x)
    5 {
    6 }
    7 String baz()
    8 {
    9 String ret("world");
    10 return ret;
    11 }
    12 int main()
    13 {
    14 String s0;
    15 String s1("hello");
    16 String s2(s0);
    17 String s3 = s1;
    18 s2 = s1;
    19 foo(s1);
    20 bar(s1);
    21 foo("temporary");
    22 bar("temporary");
    23 String s4 = baz();
    24 std::vector<String> svec;
    25 svec.push_back(s0);
    26 svec.push_back(s1);
    27 svec.push_back(baz());
    28 svec.push_back("good job");
    29 }
    本文给出我认为适合面试的答案,强调正确性及易实现(白板上写也不会错),不强调效率。某种意义上可以说是以时间(运行快慢)换空间(代码简洁)。

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

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

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

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

    注意代码的几个要点:

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

  • 相关阅读:
    linux驱动开发学习一:创建一个字符设备
    如何高效的对有序数组去重
    找到缺失的第一个正整数
    .NET不可变集合已经正式发布
    中国人唯一不认可的成功——就是家庭的和睦,人生的平淡【转】
    自己动手搭建 MongoDB 环境,并建立一个 .NET HelloWorld 程序测试
    ASP.NET MVC 中如何用自定义 Handler 来处理来自 AJAX 请求的 HttpRequestValidationException 错误
    自己动手搭建 Redis 环境,并建立一个 .NET HelloWorld 程序测试
    ServiceStack 介绍
    一步一步实战扩展 ASP.NET Route,实现小写 URL、个性化 URL
  • 原文地址:https://www.cnblogs.com/miercler/p/12600061.html
Copyright © 2011-2022 走看看