首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【C++重载操作符与转换】赋值操作符

【C++重载操作符与转换】赋值操作符

作者头像
byte轻骑兵
发布2026-01-21 17:03:40
发布2026-01-21 17:03:40
860
举报

在C++中,赋值操作符(operator=)是类设计中最为关键的重载操作符之一。它不仅影响对象的赋值行为,还与资源管理、深拷贝/浅拷贝、异常安全等核心特性紧密相关。

一、赋值操作符重载基础

1.1 什么是赋值操作符重载

在 C++ 里,赋值操作符 = 用于将一个对象的值赋给另一个对象。对于内置数据类型(如 intdouble 等),赋值操作是由编译器自动处理的。但对于自定义类,编译器会提供一个默认的赋值操作符,但这个默认的赋值操作符只是简单地进行浅拷贝,在某些情况下可能无法满足我们的需求,这时就需要我们自己重载赋值操作符。

1.2 默认赋值操作符

当我们定义一个类而没有显式重载赋值操作符时,编译器会为我们生成一个默认的赋值操作符。默认赋值操作符会逐个成员地进行赋值,也就是浅拷贝。下面是一个简单的示例:

代码语言:javascript
复制
#include <iostream>
class MyClass {
private:
    int data;
public:
    MyClass(int d = 0) : data(d) {}
    // 编译器会自动生成默认赋值操作符
    void display() const {
        std::cout << "Data: " << data << std::endl;
    }
};

int main() {
    MyClass obj1(10);
    MyClass obj2(20);
    obj2 = obj1;  // 使用默认赋值操作符
    obj2.display();
    return 0;
}

obj2 = obj1 调用了编译器生成的默认赋值操作符,将 obj1data 成员的值赋给了 obj2data 成员。

1.3 浅拷贝的问题

浅拷贝在处理包含指针成员的类时会出现问题。考虑下面的代码:

代码语言:javascript
复制
#include <iostream>
class MyClass {
private:
    int* data;
public:
    MyClass(int d = 0) {
        data = new int(d);
    }
    ~MyClass() {
        delete data;
    }
    void display() const {
        std::cout << "Data: " << *data << std::endl;
    }
};

int main() {
    MyClass obj1(10);
    MyClass obj2(20);
    obj2 = obj1;  // 默认赋值操作符(浅拷贝)
    obj1.display();
    obj2.display();
    return 0;
}

默认赋值操作符只是将 obj1data 指针的值赋给了 obj2data 指针,意味着两个对象的 data 指针指向了同一块内存。当其中一个对象被销毁时,会释放这块内存,而另一个对象的指针就会变成悬空指针,再次访问会导致未定义行为。

1.4 赋值操作符的调用场景

  • 显式赋值a = b;
  • 链式赋值a = b = c;(需返回*this的引用)
  • 隐式转换后赋值a = someOtherType;(需配合类型转换操作符)

1.5 赋值操作符的声明规范

代码语言:javascript
复制
class MyClass {
public:
    // 赋值操作符声明
    MyClass& operator=(const MyClass& rhs);  // 返回引用,参数为const引用
};

关键约束

  • 不能通过const修饰(会阻止成员修改)。
  • 不能重载为全局函数(必须为成员函数)。
  • 不能通过delete直接禁用(但可通过私有化实现)。

二、重载赋值操作符

2.1 基本语法

重载赋值操作符的基本语法如下:

代码语言:javascript
复制
class ClassName {
public:
    ClassName& operator=(const ClassName& other) {
        // 赋值操作的实现
        return *this;
    }
};
  • ClassName& 表示返回值类型是当前类的引用,这样可以支持链式赋值,如 obj1 = obj2 = obj3;
  • operator= 是赋值操作符重载的函数名。
  • const ClassName& other 是参数,通常使用 const 引用,以避免修改传入的对象。

2.2 深拷贝实现

为了解决浅拷贝的问题,可以重载赋值操作符,实现深拷贝。下面是一个改进后的代码:

代码语言:javascript
复制
#include <iostream>
class MyClass {
private:
    int* data;
public:
    MyClass(int d = 0) {
        data = new int(d);
    }
    ~MyClass() {
        delete data;
    }
    MyClass& operator=(const MyClass& other) {
        if (this != &other) {  // 避免自我赋值
            delete data;  // 释放当前对象的内存
            data = new int(*other.data);  // 分配新内存并复制值
        }
        return *this;
    }
    void display() const {
        std::cout << "Data: " << *data << std::endl;
    }
};

int main() {
    MyClass obj1(10);
    MyClass obj2(20);
    obj2 = obj1;  // 调用重载的赋值操作符
    obj1.display();
    obj2.display();
    return 0;
}

重载的赋值操作符首先检查是否是自我赋值(this != &other),如果不是,则释放当前对象的内存,然后分配新的内存并复制传入对象的值,实现了深拷贝。

2.3 自我赋值检查

自我赋值检查是重载赋值操作符时的一个重要步骤。如果不进行自我赋值检查,当执行 obj = obj; 时,会先释放当前对象的内存,然后再尝试复制已经被释放的内存,导致未定义行为。因此,在重载赋值操作符时,应该始终检查是否是自我赋值。

三、赋值操作符重载的其他形式

3.1 不同类型之间的赋值

除了对象之间的赋值,还可以重载赋值操作符,实现不同类型之间的赋值。例如,将一个 int 类型的值赋给自定义类的对象:

代码语言:javascript
复制
#include <iostream>
class MyClass {
private:
    int data;
public:
    MyClass(int d = 0) : data(d) {}
    MyClass& operator=(int value) {
        data = value;
        return *this;
    }
    void display() const {
        std::cout << "Data: " << data << std::endl;
    }
};

int main() {
    MyClass obj(10);
    obj = 20;  // 调用重载的赋值操作符
    obj.display();
    return 0;
}

重载了赋值操作符,使得可以将一个 int 类型的值赋给 MyClass 类型的对象。

3.2 复合赋值操作符重载

复合赋值操作符(如 +=-=*= 等)也可以被重载。下面是一个重载 += 操作符的示例:

代码语言:javascript
复制
#include <iostream>
class MyClass {
private:
    int data;
public:
    MyClass(int d = 0) : data(d) {}
    MyClass& operator+=(const MyClass& other) {
        data += other.data;
        return *this;
    }
    void display() const {
        std::cout << "Data: " << data << std::endl;
    }
};

int main() {
    MyClass obj1(10);
    MyClass obj2(20);
    obj1 += obj2;  // 调用重载的 += 操作符
    obj1.display();
    return 0;
}

重载的 += 操作符将两个对象的 data 成员相加,并将结果存储在当前对象中。

四、赋值操作符重载的注意事项

4.1 只能重载为成员函数

赋值操作符 = 只能重载为类的成员函数,不能重载为非成员函数。这是因为赋值操作符的第一个操作数必须是当前对象(*this),如果重载为非成员函数,就无法满足这个要求。

4.2 避免内存泄漏

在重载赋值操作符时,要特别注意内存管理。如果类中包含指针成员,应该在赋值操作前释放当前对象的内存,避免内存泄漏。

4.3 异常安全性

在重载赋值操作符时,要考虑异常安全性。如果在分配新内存时抛出异常,应该保证对象的状态不变。可以使用 “拷贝并交换” 技术来实现异常安全的赋值操作符。

五、“拷贝并交换” 技术

5.1 原理

“拷贝并交换” 技术是一种实现异常安全的赋值操作符的常用方法。其基本原理是:先创建一个临时对象,将传入对象的值复制到临时对象中,然后交换当前对象和临时对象的资源。如果在复制过程中抛出异常,当前对象的状态不会改变。

5.2 代码示例

代码语言:javascript
复制
#include <iostream>
#include <algorithm>  // 包含 std::swap

class MyClass {
private:
    int* data;
    int size;
public:
    MyClass(int s = 0) : size(s) {
        data = new int[size];
    }
    ~MyClass() {
        delete[] data;
    }
    MyClass(const MyClass& other) : size(other.size) {
        data = new int[size];
        std::copy(other.data, other.data + size, data);
    }
    void swap(MyClass& other) {
        std::swap(data, other.data);
        std::swap(size, other.size);
    }
    MyClass& operator=(MyClass other) {  // 传值调用,会调用拷贝构造函数
        this->swap(other);
        return *this;
    }
    void display() const {
        std::cout << "Size: " << size << std::endl;
    }
};

int main() {
    MyClass obj1(10);
    MyClass obj2(20);
    obj2 = obj1;  // 调用重载的赋值操作符
    obj2.display();
    return 0;
}

赋值操作符的参数采用传值调用,会调用拷贝构造函数创建一个临时对象。然后通过 swap 函数交换当前对象和临时对象的资源,最后临时对象在函数结束时自动销毁,释放其占用的内存。

六、总结

赋值操作符重载是 C++ 中一个重要的特性,它允许我们为自定义类重新定义赋值操作的行为。在重载赋值操作符时,要注意避免浅拷贝带来的问题,进行自我赋值检查,考虑不同类型之间的赋值和复合赋值操作符的重载。同时,要遵循只能重载为成员函数的规则,注意内存管理和异常安全性。“拷贝并交换” 技术是一种实现异常安全的赋值操作符的有效方法。通过掌握赋值操作符重载的相关知识,可以编写出更加健壮和灵活的 C++ 代码。

希望本文能帮助你更好地理解和掌握 C++ 中赋值操作符重载的相关内容。如果你有任何疑问或建议,欢迎在评论区留言。

七、相关代码总结

7.1 深拷贝赋值操作符重载代码

代码语言:javascript
复制
#include <iostream>
class MyClass {
private:
    int* data;
public:
    MyClass(int d = 0) {
        data = new int(d);
    }
    ~MyClass() {
        delete data;
    }
    MyClass& operator=(const MyClass& other) {
        if (this != &other) {  // 避免自我赋值
            delete data;  // 释放当前对象的内存
            data = new int(*other.data);  // 分配新内存并复制值
        }
        return *this;
    }
    void display() const {
        std::cout << "Data: " << *data << std::endl;
    }
};

int main() {
    MyClass obj1(10);
    MyClass obj2(20);
    obj2 = obj1;  // 调用重载的赋值操作符
    obj1.display();
    obj2.display();
    return 0;
}

7.2 不同类型赋值操作符重载代码

代码语言:javascript
复制
#include <iostream>
class MyClass {
private:
    int data;
public:
    MyClass(int d = 0) : data(d) {}
    MyClass& operator=(int value) {
        data = value;
        return *this;
    }
    void display() const {
        std::cout << "Data: " << data << std::endl;
    }
};

int main() {
    MyClass obj(10);
    obj = 20;  // 调用重载的赋值操作符
    obj.display();
    return 0;
}

7.3 复合赋值操作符重载代码

代码语言:javascript
复制
#include <iostream>
class MyClass {
private:
    int data;
public:
    MyClass(int d = 0) : data(d) {}
    MyClass& operator+=(const MyClass& other) {
        data += other.data;
        return *this;
    }
    void display() const {
        std::cout << "Data: " << data << std::endl;
    }
};

int main() {
    MyClass obj1(10);
    MyClass obj2(20);
    obj1 += obj2;  // 调用重载的 += 操作符
    obj1.display();
    return 0;
}

7.4 “拷贝并交换” 技术实现赋值操作符重载代码

代码语言:javascript
复制
#include <iostream>
#include <algorithm>  // 包含 std::swap

class MyClass {
private:
    int* data;
    int size;
public:
    MyClass(int s = 0) : size(s) {
        data = new int[size];
    }
    ~MyClass() {
        delete[] data;
    }
    MyClass(const MyClass& other) : size(other.size) {
        data = new int[size];
        std::copy(other.data, other.data + size, data);
    }
    void swap(MyClass& other) {
        std::swap(data, other.data);
        std::swap(size, other.size);
    }
    MyClass& operator=(MyClass other) {  // 传值调用,会调用拷贝构造函数
        this->swap(other);
        return *this;
    }
    void display() const {
        std::cout << "Size: " << size << std::endl;
    }
};

int main() {
    MyClass obj1(10);
    MyClass obj2(20);
    obj2 = obj1;  // 调用重载的赋值操作符
    obj2.display();
    return 0;
}

八、附录:操作符重载对比表

操作符

典型场景

返回值类型

参数类型

是否必须为成员函数

operator=

对象赋值

T&

const T&

必须

operator=(T&&)

移动赋值(C++11)

T&

T&&

必须

operator+

算术加法

T(非引用)

const T&, const T&

可选(通常非成员)

operator==

相等比较

bool

const T&, const T&

可选(通常非成员)

operator<

排序比较

bool

const T&, const T&

可选(通常非成员)


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-05-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、赋值操作符重载基础
    • 1.1 什么是赋值操作符重载
    • 1.2 默认赋值操作符
    • 1.3 浅拷贝的问题
    • 1.4 赋值操作符的调用场景
    • 1.5 赋值操作符的声明规范
  • 二、重载赋值操作符
    • 2.1 基本语法
    • 2.2 深拷贝实现
    • 2.3 自我赋值检查
  • 三、赋值操作符重载的其他形式
    • 3.1 不同类型之间的赋值
    • 3.2 复合赋值操作符重载
  • 四、赋值操作符重载的注意事项
    • 4.1 只能重载为成员函数
    • 4.2 避免内存泄漏
    • 4.3 异常安全性
  • 五、“拷贝并交换” 技术
    • 5.1 原理
    • 5.2 代码示例
  • 六、总结
  • 七、相关代码总结
    • 7.1 深拷贝赋值操作符重载代码
    • 7.2 不同类型赋值操作符重载代码
    • 7.3 复合赋值操作符重载代码
    • 7.4 “拷贝并交换” 技术实现赋值操作符重载代码
  • 八、附录:操作符重载对比表
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档