后端系统开发之工作和面试中的字符串
软件开发过程中会遇到形形色色的字符串处理问题,例如数字和字符串之间的格式转换、字符串去掉前后空白字符、从一个特定格式的字符串中提取字符串等。上面这些函数通常会出现在各大公司的基础库中,使用起来非常方便。
今天要分享的是一个去掉字符串结尾换行符的小技巧,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 让对方认为与自己有关
若对方不认为与自己有关,文案就无法感动人心。
——摘自《好文案一句话就够了》
解读:很有意思的一本广告文案技巧方面的书。