字节二面面试题:使用实现一个基础的 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()