后端系统开发之工作和面试中的字符串

软件开发过程中会遇到形形色色的字符串处理问题,例如数字和字符串之间的格式转换、字符串去掉前后空白字符、从一个特定格式的字符串中提取字符串等。上面这些函数通常会出现在各大公司的基础库中,使用起来非常方便。

今天要分享的是一个去掉字符串结尾换行符的小技巧,C++11提供了back()函数用于返回最后一个字符,以及pop_back()函数删除最后一个字符。

因此去掉字符串结尾换行符可以写的非常优雅:

std::string name_str("Test\n");
if (name_str.back() == '\n') {
  name_str.pop_back();
}

没有对比就没有伤害,在C++11之前我们得这样实现:首先通过string.at(string.length()-1)获取最后一个字符(或者string[string.length()-1],at()和[]等效),然后调用erase()函数删除最后一个字符。

感受一下“怀旧版”代码:

if (name_str.at(name_str.length() - 1) == '\n') {
  name_str.erase(name_str.begin() + name_str.length() - 1);
}

推荐大家在工作中有意识的使用C++11的新函数,能让代码看起来更加简洁清爽。

关于字符串的面试题非常多,这里分享一个白板面试经典题目“如何实现一个自定义的string类”。曾经有两家互联网公司都让我在白纸上写自定义string类的实现,因此留下很深的印象。

实现一个自定义的string类并不难,但是这个题目考察的地方很细:

构造函数中如何new一个字符数组,析构函数中怎么delete。技术细节:delete []才是释放数组空间的正确姿势。

如何实现类的拷贝赋值运算符函数。技术细节:返回值应该是类的引用,因此最后一定是return *this。

如何实现字符串类的“+=”和“+”运算符重载。技术细节:“+=”是一元运算符,它会改变字符串内部数据,因此应该设计成类的成员函数member function;“+”是二元运算符,具有加法的对称性,例如字符串a+b和b+a的结果是一样的,因此应该设计成类的非成员函数non-member function,即友元函数。

下面是按上述思路写的白板实现:

class String {
 private:
  size_t length_;
  char* data_;

 public:
  // default constructor
  String() :
      length_(0),
      data_(new char[1]) {
    data_[0] = '\0'; 
  }

  // constructor
  String(const char* str) :
      length_(0) {
    length_ = strlen(str);
    data_ = new char[length_ + 1];
    strcpy(data_, str);
  }

  // destructor
  ~String() {
    length_ = 0;
    delete [] data_;
    data_ = nullptr;
  }

  // copy constructor
  String(const String& other) :
      length_(other.length_),
      data_(new char[length_ + 1]) {
    strcpy(data_, other.data_);
  }

  // copy assignment operator
  String& operator=(const String& other) {
    if (this == &other) {
      return *this;
    }

    length_ = other.length_;
    delete [] data_;
    data_ = new char[length_ + 1];
    strcpy(data_, other.data_);
    return *this;
  }

  // operator+=, member function
  String& operator+=(const String& other) {
    String tmp(data_);
    length_ += other.length_;
    delete [] data_;
    data_ = new char[length_ + 1];
    memcpy(data_, tmp.data_, tmp.length_);
    memcpy(data_ + tmp.length_, other.data_, other.length_);
    return *this;
  }

  // operator+, non-member function
  friend String operator+(const String& lhs,
                          const String& rhs) {
    String tmp;
    tmp.length_ = lhs.length_ + rhs.length_;
    delete [] tmp.data_;
    tmp.data_ = new char[tmp.length_ + 1];
    memcpy(tmp.data_, lhs.data_, lhs.length_);
    memcpy(tmp.data_ + lhs.length_, rhs.data_, rhs.length_);
    return tmp;
  }
};

如果面试官还让写移动构造函数和移动赋值运算符,说明是想考察面试者的C++11知识。这两个函数用于对右值(临时对象)进行拷贝和赋值,其实现要点是使用std::move函数。

C++11加入了右值引用(rvalue reference)的概念(用&&标识),用来区分对左值和右值的引用。左值就是一个有名字的对象,而右值则是一个无名对象(临时对象)。move语义允许修改右值(以前右值被看作是不可修改的,等同于const T&类型)。

如果你用来初始化或拷贝的源对象是个右值(临时对象)会怎么样呢?你仍然需要拷贝它的值,但随后很快右值就会被释放。这意味着产生了额外的操作开销,包括原本并不需要的空间分配以及内存拷贝。

以上两段文字摘自“C++开发者都应该使用的10个C++11特性”,文章地址:http://blog.jobbole.com/44015/

移动构造函数和移动赋值运算符的示例代码如下:

// move constructor
String(String&& temp) :
    length_(temp.length_),
    data_(std::move(temp.data_)) {
  temp.length_ = 0;
  temp.data_ = nullptr;
}

// move assignment operator
String& operator=(String&& temp) {
  if (this == &temp) {
    return *this;
  }

  length_ = temp.length_;
  delete [] data_;
  data_ = std::move(temp.data_);

  temp.length_ = 0;
  temp.data_ = nullptr;
  return *this;
}

金句分享

技巧01 让对方认为与自己有关

若对方不认为与自己有关,文案就无法感动人心。

——摘自《好文案一句话就够了》

解读:很有意思的一本广告文案技巧方面的书。

猜你喜欢

转载自blog.csdn.net/wanfang323/article/details/88047551
今日推荐