自定义字符串类示例代码
#include <iostream>
#include <cstring>
class MyString {
private:
char* data;
size_t length;
public:
// 构造函数
MyString(const char* str = nullptr) {
if (str == nullptr) {
length = 0;
data = new char[1];
data[0] = '\0';
} else {
length = std::strlen(str);
data = new char[length + 1];
std::strcpy(data, str);
}
}
// 析构函数
~MyString() {
delete[] data;
}
// 拷贝构造函数
MyString(const MyString& other) {
length = other.length;
data = new char[length + 1];
std::strcpy(data, other.data);
}
// 拷贝赋值运算符重载
MyString& operator=(const MyString& other) {
if (this == &other) {
return *this;
}
delete[] data;
length = other.length;
data = new char[length + 1];
std::strcpy(data, other.data);
return *this;
}
// 移动构造函数
MyString(MyString&& other) noexcept {
data = other.data;
length = other.length;
other.data = nullptr;
other.length = 0;
}
// 移动赋值运算符重载
MyString& operator=(MyString&& other) noexcept {
if (this == &other) {
return *this;
}
delete[] data;
data = other.data;
length = other.length;
other.data = nullptr;
other.length = 0;
return *this;
}
void print() const {
std::cout << data << std::endl;
}
};
拷贝赋值运算符重载与移动赋值运算符重载的区别
- 性能区别
- 拷贝赋值运算符重载:会进行深拷贝,即分配新的内存并将源对象的数据逐字节复制到新内存中。这涉及内存分配和数据复制操作,性能开销较大,尤其是当对象包含大量数据时。
- 移动赋值运算符重载:执行的是浅拷贝,只是将源对象的资源(如指针)转移到目标对象,然后将源对象的资源指针设为
nullptr
。没有新的内存分配和数据复制,性能更高。
- 资源管理区别
- 拷贝赋值运算符重载:源对象和目标对象各自拥有独立的资源副本,修改其中一个对象不会影响另一个。
- 移动赋值运算符重载:源对象的资源被转移给目标对象,源对象处于一个有效但可能无意义的状态(如指针为
nullptr
)。源对象的析构函数不会释放已转移的资源,避免了重复释放内存的问题。
移动语义在现代C++编程中的重要性
- 提高性能:在对象传递和赋值过程中,避免不必要的数据拷贝,特别是对于大对象或动态分配内存的对象,显著提升程序性能。
- 优化容器操作:标准库容器(如
std::vector
、std::list
等)在插入、删除或重新分配元素时,利用移动语义提高效率。例如,当 std::vector
需要重新分配内存时,会使用移动操作将旧元素转移到新内存位置,而不是拷贝。
- 支持右值引用:移动语义是右值引用的重要应用,使C++ 能够更好地区分左值和右值,在函数重载和模板匹配时提供更灵活高效的选择,进一步优化代码性能。