一、赋值运算符基础
1.1 赋值运算符的基本形式
class AssignmentBasics {
private:
int* data;
size_t size;
std::string name;
public:
// 标准的拷贝赋值运算符
AssignmentBasics& operator=(const AssignmentBasics& other) {
std::cout << "Copy assignment operator called\n";
// 1. 自赋值检查(重要!)
if (this == &other) {
return *this;
}
// 2. 释放当前对象的资源
delete[] data;
// 3. 分配新资源并复制
size = other.size;
name = other.name;
data = new int[size];
std::copy(other.data, other.data + size, data);
// 4. 返回*this的引用(支持链式赋值)
return *this;
}
// 构造函数
AssignmentBasics(size_t s = 0, const std::string& n = "default")
: size(s), name(n), data(s ? new int[s]() : nullptr) {
std::cout << "Constructor called for " << name << "\n";
}
// 拷贝构造函数(必须配合实现)
AssignmentBasics(const AssignmentBasics& other)
: size(other.size), name(other.name + "_copy") {
data = new int[size];
std::copy(other.data, other.data + size, data);
std::cout << "Copy constructor called\n";
}
// 析构函数
~AssignmentBasics() {
delete[] data;
std::cout << "Destructor called for " << name << "\n";
}
// 辅助函数
void print() const {
std::cout << name << ": [";
for (size_t i = 0; i < size; ++i) {
std::cout << data[i] << (i < size-1 ? ", " : "");
}
std::cout << "]\n";
}
};
// 使用示例
void demonstrateBasics() {
AssignmentBasics obj1(5, "obj1");
AssignmentBasics obj2(3, "obj2");
AssignmentBasics obj3(4, "obj3");
// 简单赋值
obj2 = obj1;
// 链式赋值
obj3 = obj2 = obj1;
// 自赋值
obj1 = obj1; // 应该安全处理
}1.2 赋值运算符的特性
class AssignmentProperties {
public:
int value;
// 赋值运算符必须是成员函数
AssignmentProperties& operator=(const AssignmentProperties& other) {
value = other.value;
return *this;
}
// 错误:不能定义为非成员函数
// friend AssignmentProperties& operator=(AssignmentProperties& lhs,
// const AssignmentProperties& rhs);
// 赋值运算符不能被继承
// 每个类都有自己的赋值运算符
// 如果不提供,编译器会生成默认的赋值运算符
// 默认赋值运算符执行成员逐一赋值
};
// 编译器生成的默认赋值运算符
class DefaultAssignment {
int a;
double b;
std::string c;
// 编译器生成的赋值运算符等价于:
// DefaultAssignment& operator=(const DefaultAssignment& other) {
// a = other.a;
// b = other.b;
// c = other.c;
// return *this;
// }
};二、异常安全的赋值运算符
2.1 Copy-and-Swap惯用法
class CopyAndSwap {
private:
int* data;
size_t size;
public:
// 构造函数
explicit CopyAndSwap(size_t s = 0)
: size(s), data(s ? new int[s]() : nullptr) {}
// 拷贝构造函数
CopyAndSwap(const CopyAndSwap& other)
: size(other.size), data(other.size ? new int[other.size] : nullptr) {
if (data) {
std::copy(other.data, other.data + size, data);
}
}
// 移动构造函数
CopyAndSwap(CopyAndSwap&& other) noexcept
: size(std::exchange(other.size, 0))
, data(std::exchange(other.data, nullptr)) {}
// 析构函数
~CopyAndSwap() {
delete[] data;
}
// swap函数(不抛出异常)
void swap(CopyAndSwap& other) noexcept {
using std::swap;
swap(size, other.size);
swap(data, other.data);
}
// 方法1:统一的赋值运算符(按值传递)
CopyAndSwap& operator=(CopyAndSwap other) { // 注意:按值传递
swap(other);
return *this;
}
/* 方法2:分别实现拷贝和移动赋值
// 拷贝赋值运算符
CopyAndSwap& operator=(const CopyAndSwap& other) {
CopyAndSwap temp(other); // 拷贝构造临时对象
swap(temp); // 交换内容
return *this; // temp析构时清理旧资源
}
// 移动赋值运算符
CopyAndSwap& operator=(CopyAndSwap&& other) noexcept {
CopyAndSwap temp(std::move(other)); // 移动构造临时对象
swap(temp);
return *this;
}
*/
};
// 为类定义专门的swap函数
inline void swap(CopyAndSwap& a, CopyAndSwap& b) noexcept {
a.swap(b);
}2.2 强异常保证的实现
class StrongExceptionSafety {
private:
int* data;
size_t size;
size_t capacity;
std::string metadata;
public:
StrongExceptionSafety& operator=(const StrongExceptionSafety& other) {
// 强异常保证:要么完全成功,要么状态不变
if (this != &other) {
// 1. 先分配新资源(可能抛出异常)
int* newData = nullptr;
if (other.size > 0) {
newData = new int[other.capacity]; // 可能抛出bad_alloc
// 2. 复制数据(使用不抛出异常的操作)
std::copy(other.data, other.data + other.size, newData);
}
// 3. 所有可能抛出异常的操作完成后,执行不抛出异常的操作
delete[] data; // noexcept
// 4. 更新成员(不抛出异常)
data = newData;
size = other.size;
capacity = other.capacity;
metadata = other.metadata; // string的赋值提供强异常保证
}
return *this;
}
// 使用临时变量的另一种实现
StrongExceptionSafety& operator=(const StrongExceptionSafety& other) {
if (this != &other) {
// 创建临时变量(可能抛出异常)
StrongExceptionSafety temp(other);
// 使用noexcept操作交换
using std::swap;
swap(data, temp.data);
swap(size, temp.size);
swap(capacity, temp.capacity);
swap(metadata, temp.metadata);
// temp析构时清理旧资源
}
return *this;
}
};三、移动赋值运算符
3.1 移动赋值运算符的实现
class MoveAssignment {
private:
int* data;
size_t size;
std::vector<std::string> strings;
std::unique_ptr<double[]> uniqueData;
public:
// 移动赋值运算符(应该声明为noexcept)
MoveAssignment& operator=(MoveAssignment&& other) noexcept {
std::cout << "Move assignment operator called\n";
// 1. 自移动检查
if (this == &other) {
return *this;
}
// 2. 释放当前资源
delete[] data;
// 3. 窃取资源(移动)
data = std::exchange(other.data, nullptr);
size = std::exchange(other.size, 0);
strings = std::move(other.strings);
uniqueData = std::move(other.uniqueData);
// 4. other现在处于有效但未指定的状态
return *this;
}
// 构造函数
MoveAssignment(size_t s = 0)
: size(s)
, data(s ? new int[s]() : nullptr)
, uniqueData(std::make_unique<double[]>(s)) {}
// 移动构造函数
MoveAssignment(MoveAssignment&& other) noexcept
: data(std::exchange(other.data, nullptr))
, size(std::exchange(other.size, 0))
, strings(std::move(other.strings))
, uniqueData(std::move(other.uniqueData)) {}
// 析构函数
~MoveAssignment() {
delete[] data;
}
};
// 测试移动赋值
void testMoveAssignment() {
MoveAssignment obj1(100);
MoveAssignment obj2(200);
// 移动赋值
obj2 = std::move(obj1); // obj1变为移后状态
// 从临时对象移动赋值
obj2 = MoveAssignment(300);
// 链式移动赋值
MoveAssignment obj3(400);
obj3 = std::move(obj2 = std::move(obj1));
}3.2 完美的五法则实现
class RuleOfFive {
private:
char* buffer;
size_t length;
mutable size_t useCount; // 用于跟踪
void log(const std::string& msg) const {
std::cout << msg << " [length=" << length << "]\n";
}
public:
// 1. 普通构造函数
explicit RuleOfFive(const std::string& str = "")
: length(str.length())
, buffer(length ? new char[length + 1] : nullptr)
, useCount(0) {
if (buffer) {
std::copy(str.begin(), str.end(), buffer);
buffer[length] = '\0';
}
log("Constructor");
}
// 2. 析构函数
~RuleOfFive() {
delete[] buffer;
log("Destructor");
}
// 3. 拷贝构造函数
RuleOfFive(const RuleOfFive& other)
: length(other.length)
, buffer(other.length ? new char[other.length + 1] : nullptr)
, useCount(0) {
if (buffer) {
std::copy(other.buffer, other.buffer + length + 1, buffer);
}
log("Copy constructor");
}
// 4. 拷贝赋值运算符
RuleOfFive& operator=(const RuleOfFive& other) {
log("Copy assignment");
if (this != &other) {
// 使用copy-and-swap
RuleOfFive temp(other);
swap(temp);
}
return *this;
}
// 5. 移动构造函数
RuleOfFive(RuleOfFive&& other) noexcept
: buffer(std::exchange(other.buffer, nullptr))
, length(std::exchange(other.length, 0))
, useCount(std::exchange(other.useCount, 0)) {
log("Move constructor");
}
// 6. 移动赋值运算符
RuleOfFive& operator=(RuleOfFive&& other) noexcept {
log("Move assignment");
if (this != &other) {
delete[] buffer;
buffer = std::exchange(other.buffer, nullptr);
length = std::exchange(other.length, 0);
useCount = std::exchange(other.useCount, 0);
}
return *this;
}
// swap辅助函数
void swap(RuleOfFive& other) noexcept {
using std::swap;
swap(buffer, other.buffer);
swap(length, other.length);
swap(useCount, other.useCount);
}
// 获取C字符串
const char* c_str() const {
++useCount;
return buffer ? buffer : "";
}
// 获取使用次数
size_t getUseCount() const { return useCount; }
};四、特殊类型的赋值运算符
4.1 不同类型的赋值
class FlexibleAssignment {
private:
std::variant<int, double, std::string> data;
public:
// 从int赋值
FlexibleAssignment& operator=(int value) {
data = value;
return *this;
}
// 从double赋值
FlexibleAssignment& operator=(double value) {
data = value;
return *this;
}
// 从string赋值
FlexibleAssignment& operator=(const std::string& value) {
data = value;
return *this;
}
// 从const char*赋值
FlexibleAssignment& operator=(const char* value) {
data = std::string(value);
return *this;
}
// 模板赋值运算符
template<typename T>
FlexibleAssignment& operator=(const std::vector<T>& vec) {
std::stringstream ss;
ss << "[";
for (size_t i = 0; i < vec.size(); ++i) {
ss << vec[i];
if (i < vec.size() - 1) ss << ", ";
}
ss << "]";
data = ss.str();
return *this;
}
// 访问数据
void print() const {
std::visit([](const auto& value) {
std::cout << value << "\n";
}, data);
}
};
// 使用示例
void testFlexibleAssignment() {
FlexibleAssignment fa;
fa = 42; // int赋值
fa = 3.14; // double赋值
fa = "Hello"; // const char*赋值
fa = std::string("World"); // string赋值
fa = std::vector<int>{1, 2, 3}; // vector赋值
}4.2 复合赋值运算符
class CompoundAssignment {
private:
double value;
public:
CompoundAssignment(double v = 0.0) : value(v) {}
// 算术复合赋值
CompoundAssignment& operator+=(const CompoundAssignment& rhs) {
value += rhs.value;
return *this;
}
CompoundAssignment& operator-=(const CompoundAssignment& rhs) {
value -= rhs.value;
return *this;
}
CompoundAssignment& operator*=(const CompoundAssignment& rhs) {
value *= rhs.value;
return *this;
}
CompoundAssignment& operator/=(const CompoundAssignment& rhs) {
if (rhs.value != 0) {
value /= rhs.value;
} else {
throw std::domain_error("Division by zero");
}
return *this;
}
// 支持与标量的复合赋值
CompoundAssignment& operator+=(double scalar) {
value += scalar;
return *this;
}
CompoundAssignment& operator*=(double scalar) {
value *= scalar;
return *this;
}
double getValue() const { return value; }
};
// 基于复合赋值实现算术运算符
CompoundAssignment operator+(CompoundAssignment lhs,
const CompoundAssignment& rhs) {
return lhs += rhs; // 利用复合赋值
}
CompoundAssignment operator*(CompoundAssignment lhs, double scalar) {
return lhs *= scalar;
}4.3 位运算复合赋值
class BitOperations {
private:
unsigned int bits;
public:
BitOperations(unsigned int b = 0) : bits(b) {}
// 位运算复合赋值
BitOperations& operator&=(const BitOperations& rhs) {
bits &= rhs.bits;
return *this;
}
BitOperations& operator|=(const BitOperations& rhs) {
bits |= rhs.bits;
return *this;
}
BitOperations& operator^=(const BitOperations& rhs) {
bits ^= rhs.bits;
return *this;
}
BitOperations& operator<<=(unsigned int shift) {
bits <<= shift;
return *this;
}
BitOperations& operator>>=(unsigned int shift) {
bits >>= shift;
return *this;
}
// 设置特定位
BitOperations& setBit(unsigned int pos) {
if (pos < 32) {
bits |= (1u << pos);
}
return *this;
}
// 清除特定位
BitOperations& clearBit(unsigned int pos) {
if (pos < 32) {
bits &= ~(1u << pos);
}
return *this;
}
// 切换特定位
BitOperations& toggleBit(unsigned int pos) {
if (pos < 32) {
bits ^= (1u << pos);
}
return *this;
}
bool testBit(unsigned int pos) const {
return (pos < 32) && (bits & (1u << pos));
}
void print() const {
std::cout << "Bits: " << std::bitset<32>(bits) << " (" << bits << ")\n";
}
};五、赋值运算符与继承
5.1 基类和派生类的赋值运算符
class Base {
protected:
int* baseData;
size_t baseSize;
public:
Base(size_t size = 0)
: baseSize(size), baseData(size ? new int[size]() : nullptr) {
std::cout << "Base constructor\n";
}
virtual ~Base() {
delete[] baseData;
std::cout << "Base destructor\n";
}
// 基类拷贝赋值运算符
Base& operator=(const Base& other) {
std::cout << "Base copy assignment\n";
if (this != &other) {
delete[] baseData;
baseSize = other.baseSize;
baseData = baseSize ? new int[baseSize] : nullptr;
if (baseData) {
std::copy(other.baseData, other.baseData + baseSize, baseData);
}
}
return *this;
}
// 基类移动赋值运算符
Base& operator=(Base&& other) noexcept {
std::cout << "Base move assignment\n";
if (this != &other) {
delete[] baseData;
baseData = std::exchange(other.baseData, nullptr);
baseSize = std::exchange(other.baseSize, 0);
}
return *this;
}
virtual void print() const {
std::cout << "Base: size=" << baseSize << "\n";
}
};
class Derived : public Base {
private:
double* derivedData;
size_t derivedSize;
public:
Derived(size_t bSize = 0, size_t dSize = 0)
: Base(bSize)
, derivedSize(dSize)
, derivedData(dSize ? new double[dSize]() : nullptr) {
std::cout << "Derived constructor\n";
}
~Derived() override {
delete[] derivedData;
std::cout << "Derived destructor\n";
}
// 派生类拷贝赋值运算符
Derived& operator=(const Derived& other) {
std::cout << "Derived copy assignment\n";
if (this != &other) {
// 调用基类赋值运算符
Base::operator=(other);
// 处理派生类成员
delete[] derivedData;
derivedSize = other.derivedSize;
derivedData = derivedSize ? new double[derivedSize] : nullptr;
if (derivedData) {
std::copy(other.derivedData,
other.derivedData + derivedSize,
derivedData);
}
}
return *this;
}
// 派生类移动赋值运算符
Derived& operator=(Derived&& other) noexcept {
std::cout << "Derived move assignment\n";
if (this != &other) {
// 调用基类移动赋值运算符
Base::operator=(std::move(other));
// 处理派生类成员
delete[] derivedData;
derivedData = std::exchange(other.derivedData, nullptr);
derivedSize = std::exchange(other.derivedSize, 0);
}
return *this;
}
void print() const override {
Base::print();
std::cout << "Derived: size=" << derivedSize << "\n";
}
};
// 演示切片问题
void demonstrateSlicing() {
std::cout << "\n=== Slicing Problem ===\n";
Derived d1(5, 10);
Derived d2(3, 6);
Base b(2);
// 对象切片
b = d1; // 只调用Base::operator=,丢失派生类信息
b.print(); // 只显示基类信息
// 通过基类指针赋值
Base* pb1 = new Derived(4, 8);
Base* pb2 = new Derived(2, 4);
*pb1 = *pb2; // 仍然是切片!只调用Base::operator=
pb1->print(); // 虚函数调用正确,但数据被切片
delete pb1;
delete pb2;
}5.2 防止切片的设计
class NonSliceable {
// 方法1:使基类抽象
class AbstractBase {
protected:
int value;
public:
AbstractBase(int v = 0) : value(v) {}
virtual ~AbstractBase() = default;
// 纯虚函数使类抽象
virtual void process() = 0;
// 删除赋值运算符
AbstractBase& operator=(const AbstractBase&) = delete;
};
// 方法2:私有赋值运算符
class PrivateAssignBase {
private:
PrivateAssignBase& operator=(const PrivateAssignBase&) = default;
protected:
int value;
public:
PrivateAssignBase(int v = 0) : value(v) {}
virtual ~PrivateAssignBase() = default;
};
// 方法3:使用clone模式
class Cloneable {
protected:
int value;
public:
Cloneable(int v = 0) : value(v) {}
virtual ~Cloneable() = default;
// 虚拟克隆函数
virtual std::unique_ptr<Cloneable> clone() const = 0;
// 删除赋值运算符
Cloneable& operator=(const Cloneable&) = delete;
};
class CloneableDerived : public Cloneable {
private:
double extra;
public:
CloneableDerived(int v = 0, double e = 0.0)
: Cloneable(v), extra(e) {}
std::unique_ptr<Cloneable> clone() const override {
return std::make_unique<CloneableDerived>(*this);
}
};
};六、特殊情况和最佳实践
6.1 自赋值的处理
class SelfAssignment {
private:
int* data;
size_t size;
public:
// 方法1:传统的自赋值检查
SelfAssignment& operator=(const SelfAssignment& other) {
if (this != &other) { // 自赋值检查
delete[] data;
size = other.size;
data = new int[size];
std::copy(other.data, other.data + size, data);
}
return *this;
}
// 方法2:Copy-and-Swap(自动处理自赋值)
SelfAssignment& operator=(SelfAssignment other) { // 按值传递
swap(other); // 自赋值安全
return *this;
}
// 方法3:先分配再释放(异常安全)
SelfAssignment& operator=(const SelfAssignment& other) {
// 不需要自赋值检查
int* newData = new int[other.size];
std::copy(other.data, other.data + other.size, newData);
delete[] data;
data = newData;
size = other.size;
return *this;
}
void swap(SelfAssignment& other) noexcept {
using std::swap;
swap(data, other.data);
swap(size, other.size);
}
};6.2 性能优化技巧
class OptimizedAssignment {
private:
char* buffer;
size_t size;
size_t capacity;
public:
// 优化:重用已分配的内存
OptimizedAssignment& operator=(const OptimizedAssignment& other) {
if (this != &other) {
// 如果容量足够,重用现有缓冲区
if (capacity >= other.size) {
std::copy(other.buffer, other.buffer + other.size, buffer);
size = other.size;
} else {
// 需要重新分配
char* newBuffer = new char[other.capacity];
std::copy(other.buffer, other.buffer + other.size, newBuffer);
delete[] buffer;
buffer = newBuffer;
size = other.size;
capacity = other.capacity;
}
}
return *this;
}
// 短字符串优化(SSO)
class StringWithSSO {
private:
static constexpr size_t SSO_SIZE = 15;
union {
char sso[SSO_SIZE + 1]; // 小字符串直接存储
char* ptr; // 大字符串使用堆
};
size_t length;
bool isSSO;
public:
StringWithSSO& operator=(const StringWithSSO& other) {
if (this != &other) {
// 清理当前状态
if (!isSSO && ptr) {
delete[] ptr;
}
length = other.length;
isSSO = other.isSSO;
if (isSSO) {
std::copy(other.sso, other.sso + length + 1, sso);
} else {
ptr = new char[length + 1];
std::copy(other.ptr, other.ptr + length + 1, ptr);
}
}
return *this;
}
};
};6.3 调试和测试
class AssignmentTesting {
private:
static int copyCount;
static int moveCount;
int* data;
size_t size;
int id;
public:
AssignmentTesting(size_t s = 0)
: size(s), data(s ? new int[s]() : nullptr), id(rand()) {
std::cout << "Constructor [" << id << "]\n";
}
~AssignmentTesting() {
delete[] data;
std::cout << "Destructor [" << id << "]\n";
}
// 拷贝赋值(带计数)
AssignmentTesting& operator=(const AssignmentTesting& other) {
std::cout << "Copy assignment [" << id << "] = [" << other.id << "]\n";
++copyCount;
if (this != &other) {
AssignmentTesting temp(other);
swap(temp);
}
return *this;
}
// 移动赋值(带计数)
AssignmentTesting& operator=(AssignmentTesting&& other) noexcept {
std::cout << "Move assignment [" << id << "] = [" << other.id << "]\n";
++moveCount;
if (this != &other) {
delete[] data;
data = std::exchange(other.data, nullptr);
size = std::exchange(other.size, 0);
// id不变,保持对象身份
}
return *this;
}
void swap(AssignmentTesting& other) noexcept {
using std::swap;
swap(data, other.data);
swap(size, other.size);
swap(id, other.id);
}
static void printStats() {
std::cout << "\n=== Statistics ===\n";
std::cout << "Copy assignments: " << copyCount << "\n";
std::cout << "Move assignments: " << moveCount << "\n";
}
// 单元测试
static void runTests() {
std::cout << "\n=== Running Assignment Tests ===\n";
// 测试拷贝赋值
{
AssignmentTesting a(10), b(20);
b = a;
}
// 测试移动赋值
{
AssignmentTesting a(10), b(20);
b = std::move(a);
}
// 测试自赋值
{
AssignmentTesting a(10);
a = a;
}
// 测试链式赋值
{
AssignmentTesting a(10), b(20), c(30);
c = b = a;
}
printStats();
}
};
int AssignmentTesting::copyCount = 0;
int AssignmentTesting::moveCount = 0;赋值运算符重载是C++中重要的特性,正确实现它对于资源管理至关重要。关键点包括:处理自赋值、确保异常安全、正确管理资源、支持移动语义,以及在继承层次中正确调用基类的赋值运算符。使用Copy-and-Swap惯用法可以简化实现并提供强异常保证。