字节二面面试题:使用实现一个基础的 string
类。要求类中成员函数包含:
- 默认构造函数
- 拷贝构造函数
- 移动构造函数
- ——————
- 拷贝赋值运算
- 移动赋值运算
- ——————
- 析构函数
- ——————
- 重载
+, ==, <<
等基本运算符
实现结果:
#include <iostream>
#include <ostream>
#include <istream>
#include <cstring>
using std::ostream;
using std::istream;
class String {
public:
// 1. 默认构造函数
// 加上 const 作用:(1)不会修改指向值, (2)使得str可以指向右值
// String s("HELLO");
// String s = "HELLO";
// String s;
String(const char* str = nullptr) {
std::cout << "[" << this << "] ";
std::cout << "Call: String(const char* str = nullptr)" << std::endl;
if (str == nullptr) {
len_ = 0;
str_ = new char[1];
}
else {
len_ = strlen(str);
str_ = new char[len_ + 1];
strcpy(str_, str);
}
str_[len_] = '\0';
}
// 2. 析构函数
~String() {
std::cout << "[" << this << "] ";
std::cout << "Call: ~String()" << std::endl;
delete[] str_;
str_ = nullptr;
}
// 3.拷贝构造函数
// String s1 = s;
// String s1(s);
String(const String& other) {
std::cout << "[" << this << "] ";
std::cout << "Call: String(const String& other)" << std::endl;
this->len_ = other.len_;
this->str_ = new char[this->len_ + 1];
strcpy(this->str_, other.str_);
this->str_[this->len_] = '\0';
}
// 4. 移动构造函数
// String s1(std::move(s));
String(String&& other) noexcept { // 告诉编译器不会发生异常,便于优化;如若运行时发生异常,则调用std::terminate()函数终止.
std::cout << "[" << this << "] ";
std::cout << "Call: String(String&& other)" << std::endl;
this->len_ = other.len_;
this->str_ = other.str_;
// 阻止 other 析构数据
other.str_ = nullptr; // C++ delete nullptr 是安全的行为! 因此不影响 other 对象析构函数
}
// 5. 拷贝赋值函数
// String s1;
// s1 = s;
String& operator=(const String& other) {
std::cout << "[" << this << "] ";
std::cout << "Call: String& operator=(const String& other)" << std::endl;
if (&other == this) { // String s; s = s;
return *this;
}
delete[] this->str_; // this->str_ 至少包含 '\0'
this->len_ = other.len_;
this->str_ = new char[this->len_ + 1];
strcpy(this->str_, other.str_);
this->str_[this->len_] = '\0';
return *this;
}
// 6. 移动赋值函数
// String s1 = std::move(s);
String& operator=(String&& other) noexcept {
std::cout << "[" << this << "] ";
std::cout << "Call: String& operator=(String&& other)" << std::endl;
if (&other == this) {
return *this;
}
delete[] this->str_;
this->len_ = other.len_;
this->str_ = other.str_;
// 阻止 other 析构数据
other.str_ = nullptr; // C++ delete nullptr 是安全的行为! 因此不影响 other 对象析构函数
return *this;
}
// 7. 重载运算符
// +, ==, <<:字符串拼接、判断两个字符串是否相等、输出.
bool operator==(const String& other) {
std::cout << "Call: bool operator==(const String& other)" << std::endl;
// strcmp(str1, str2); 相等返回0,不等为-1
return (strcmp(this->str_, other.str_) == 0 && this->len_ == other.len_);
}
String& operator+(const String& other) {
this->len_ += other.len_;
char* tmp = this->str_;
this->str_ = new char[this->len_ + 1];
strcpy(this->str_, tmp);
strcat(this->str_, other.str_);
this->str_[this->len_] = '\0';
delete[] tmp;
tmp = nullptr;
return *this;
}
// 需要声明为友元函数
friend ostream& operator<<(ostream& os, const String& s) {
os << "str: " << s.str_ << ", len: " << s.len_;
return os;
}
private:
char* str_;
unsigned int len_;
};
int main() {
// 1.默认构造 String(const char* str = nullptr)
String s = "12345";
// 3. 拷贝构造 String(const String& other) or String s1 = s;
String s1(s);
// 4. 移动构造 String(String&& other)
String s2(std::move(s));
// 5. 拷贝赋值 String& operator=(const String& other)
String s3 = "ABCDE";
s = s3;
// 6. 移动赋值 String& operator=(String&& other)
s = std::move(s3);
// 7. 重载 +, ==, << String& operator+(const String& other)
String s4 = "XYZ";
s = s + s4;
if (s == s4)
std::cout << "s == s4" << std::endl;
else
std::cout << "s != s4" << std::endl;
std::cout << s << std::endl;
return 0;
}
// 运行结果
// [0x7ffee6fbb9b8] Call: String(const char* str = nullptr)
// [0x7ffee6fbb9a8] Call: String(const String& other)
// [0x7ffee6fbb988] Call: String(String&& other)
// [0x7ffee6fbb978] Call: String(const char* str = nullptr)
// [0x7ffee6fbb9b8] Call: String& operator=(const String& other)
// [0x7ffee6fbb9b8] Call: String& operator=(String&& other)
// [0x7ffee6fbb968] Call: String(const char* str = nullptr)
// [0x7ffee6fbb9b8] Call: String& operator=(const String& other)
// Call: bool operator==(const String& other)
// s != s4
// str: ABCDEXYZ, len: 8
// [0x7ffee6fbb968] Call: ~String()
// [0x7ffee6fbb978] Call: ~String()
// [0x7ffee6fbb988] Call: ~String()
// [0x7ffee6fbb9a8] Call: ~String()
// [0x7ffee6fbb9b8] Call: ~String()