c++ 重载 = 为什么返回引用类型

本文探讨了C++中运算符重载返回引用的原因及应用场景,重点解释了如何通过返回引用提高效率并维持赋值运算符的连续赋值特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文链接:http://www.cnblogs.com/codingmengmeng/p/5871254.html

事实上,我们的重载运算符返回void、返回对象本身、返回对象引用都是可以的,并不是说一定要返回一个引用,只不过在不同的情况下需要不同的返回值。

那么什么情况下要返回对象的引用呢?

原因有两个:

  •   允许进行连续赋值
  •       防止返回对象(返回对象也可以进行连续赋值)的时候调用拷贝构造函数和析构函数导致不必要的开销,降低赋值运算符等的效率

  

  对于第二点原因:如果用”值传递“的方式,虽然功能任然正确,但由于return语句要把*this拷贝到保存返回值的外部存储单元之中,增加了不必要的开销,会降低赋值函数的效率

 

  场景:

  需要返回对象引用或者返回对象(效率没有返回引用高),需要实现连续赋值,使重载的运算符更符合C++本身的运算符语意,如连续赋值 = += -= *= 、=,<<输出流

  关于赋值 =,我们知道赋值=有连续等于的特性

1 int x,y,z;
2 x=y=z=15;

  同样有趣的是,赋值采用的是右结合律,所以上述连锁赋值被解析为

1 x=(y=(z=15));//赋值连锁形式

  这里15先被赋值给z,然后其结果(更新后的z)再被赋值给y,然后其结果(更新后的y)再被赋值给x。

  为了实现”连锁赋值“,赋值操作符号必须返回一个reference(引用)指向操作符号的左侧实参(而事实上重载运算符的左侧实参就是调用对象本身,比如= += -=等),这是你为classes实现赋值操作符时应该遵循的协议:这个协议不仅仅适用于以上的标准赋值形式,也适用于所有赋值运算。

复制代码
 1 class Widght{
 2     public:
 3       .....
 4     Widget& operator=(cosnt Widget& rhs)
 5     {
 6        ...
 7        return* this;
 8     }  
 9     Widget& operator+=(cosnt Widget& rhs)
10     {
11        ...
12        return* this;
13     }  
14     
15     Widget& operator-=(cosnt Widget& rhs)
16     {
17        ...
18        return* this;
19     }  
20     
21     Widget& operator*=(cosnt Widget& rhs)
22     {
23        ...
24        return* this;
25     }  
26     
27     Widget& operator/=(cosnt Widget& rhs)
28     {
29        ...
30        return* this;
31     }  
32     ...
33  };
复制代码

  注意,这只是个协议,并无强制性,如果不遵循它,代码一样可以通过编译,然而这份协议被所有内置类型和标准程序库提供的类型入string,vector,complex,trl:shared_ptr或者即将提供的类型共同遵守。因此除非你有一个标新立异的好理由,不然还是随众吧。

 

  下面看一个赋值运算符重载的例子:

  1、首先是返回对象的情况:

复制代码
 1 #include <iostream>
 2 using namespace std;
 3 class String
 4 {
 5 private:
 6     char *str;
 7     int len;
 8 public:
 9     String(const char* s);//构造函数声明
10     String operator=(const String& another);//运算符重载,此时返回的是对象
11     void show()
12     {
13         cout << "value = " << str << endl;
14     }
15 
16     /*copy construct*/
17     String(const String& other)
18     {
19         len = other.len;
20         str = new char[len + 1];
21         strcpy(str, other.str);
22         cout << "copy construct" << endl;
23     }
24 
25     ~String()
26     {
27         delete[] str;
28         cout << "deconstruct" << endl;
29     }
30 };
31 
32 String::String(const char* s)//构造函数定义
33 {
34     len = strlen(s);
35     str = new char[len + 1];
36     strcpy(str, s);
37 }
38 
39 String String::operator=(const String &other)//运算符重载
40 {
41     if (this == &other)
42         return *this;
43 //        return;
44     delete[] str;
45     len = other.len;
46     str = new char[len + 1];
47     strcpy(str, other.str);
48     return *this;
49 //    return;
50 }
51 
52 int main()
53 {
54     String str1("abc");
55     String str2("123");
56     String str3("456");
57     str1.show();
58     str2.show();
59     str3.show();
60     str3 = str1 = str2;//str3.operator=(str1.operator=str2)    
61     str3.show();
62     str1.show();
63     return 0;
64 }
复制代码

  运行结果:

  

 

  2、下面是返回引用的情况(String& operator+(const String& str)),直接贴运行结果:

  

  

  当运算符重载返回的是对象时,会在赋值运算过程的返回途中调用两次拷贝构造函数和析构函数(因为return的是个新的对象)

  如果采用String& operator+(const String& str)这样就不会有多余的调用(因为这里直接return一个已经存在的对象的引用

  上面的栗子也说明一点:析构函数的调用是在变量作用域结束的时候(以及程序运行结束的时候)

  如果采用return对象,那么第二次赋值运算调用的情况就是

  将一个新的String对象传递到operator+()的参数中去 相当于 

const String&str = returnStringObj;

  如果采用return对象引用,那么第二次赋值运算的情况就是

  将一个已经存在的String对象(其实就是str1)的引用传递给operator+()的参数中去

const String&str = returnReference; //(String& returnReference = str1;)

  +=等运算符也是同样的考虑,比如

复制代码
 1 int main()
 2 {
 3     String str1("abc");
 4     String str2("123");
 5     String str3("456");
 6     str1.show();
 7     str2.show();
 8     str3.show();
 9     str3 = str1 = str2;//str3.operator=(str1.operator=str2)    
10     str3.show();
11     str1.show();
12 
13     int num = 10;
14     num += (num += 100);
15     cout << num << endl;
16     return 0;
17 }
复制代码

  

  如果要这样使用+=或其它上面举出的运算符,则这些运算符的返回值一定要是一个对象或者引用才行,不然就会出现错误(参数类型不符合)。什么意思呢,下面举个栗子

  我现在让运算符重载返回的类型为空,单个赋值,不使用连续赋值

复制代码
 1 #include <iostream>
 2 using namespace std;
 3 class String
 4 {
 5 private:
 6     char *str;
 7     int len;
 8 public:
 9     String(const char* s);//构造函数声明
10     void operator=(const String& another);//运算符重载,此时返回为空
11     void show()
12     {
13         cout << "value = " << str << endl;
14     }
15 
16     /*copy construct*/
17     String(const String& other)
18     {
19         len = other.len;
20         str = new char[len + 1];
21         strcpy(str, other.str);
22         cout << "copy construct" << endl;
23     }
24 
25     ~String()
26     {
27         delete[] str;
28         cout << "deconstruct" << endl;
29     }
30 };
31 
32 String::String(const char* s)
33 {
34     len = strlen(s);
35     str = new char[len + 1];
36     strcpy(str, s);
37 }
38 
39 void String::operator=(const String &other)
40 {
41     if (this == &other)
42 //        return *this;
43         return;
44     delete[] str;
45     len = other.len;
46     str = new char[len + 1];
47     strcpy(str, other.str);
48 //    return *this;
49     return;
50 }
51 
52 int main()
53 {
54     String str1("abc");
55     String str2("123");
56     String str3("456");
57     str1.show();
58     str2.show();
59     str3.show();
60     str3 = str1;//这样OK
61     str3.show();
62     str1.show();
63     return 0;
64 }
复制代码

  运行结果:

   

  但当我把主函数中str1,str2,str3改为连续赋值时:

复制代码
 1 int main()
 2 {
 3     String str1("abc");
 4     String str2("123");
 5     String str3("456");
 6     str1.show();
 7     str2.show();
 8     str3.show();
 9     str3 = str1=str2;//这样不OK
10     str3.show();
11     str1.show();
12     return 0;
13 }
复制代码

  出错:

  

  所以,当你确定不使用连续赋值时,直接返回void也是可以的。要明白一点:

  运算符左侧的对象就是操作对象,比如

1 ObjectA = ObjectB 等同于ObjectA.operator=(ObjectB) 
2 ObjectA+=ObjectB 等同于ObjectA.operator+(ObjectB)

  

  最后要说明一点:并非必须返回引用,返回引用的好处既可以避免拷贝构造函数和析构函数的调用,又可以保证= +=等运算符的原始语义清晰

  啥叫原始语义清晰呢?

  

1 (str3 = str1) = str2;

  我们的意识里,就是先执行括号内容,即str1赋值给str3,然后str2再赋值给str3,最后str3输出的内容是str2的。

  即如果运算符重载返回的是对象引用时,

复制代码
 1 //返回的是对象引用的情况
 2 #include <iostream>
 3 using namespace std;
 4 class String
 5 {
 6 private:
 7     char *str;
 8     int len;
 9 public:
10     String(const char* s);//构造函数声明
11     String& operator=(const String& another);//运算符重载,此时返回为空
12     void show()
13     {
14         cout << "value = " << str << endl;
15     }
16 
17     /*copy construct*/
18     String(const String& other)
19     {
20         len = other.len;
21         str = new char[len + 1];
22         strcpy(str, other.str);
23         cout << "copy construct" << endl;
24     }
25 
26     ~String()
27     {
28         delete[] str;
29         cout << "deconstruct" << endl;
30     }
31 };
32 
33 String::String(const char* s)
34 {
35     len = strlen(s);
36     str = new char[len + 1];
37     strcpy(str, s);
38 }
39 
40 String& String::operator=(const String &other)
41 {
42     if (this == &other)
43         return *this;
44 //        return;
45     delete[] str;
46     len = other.len;
47     str = new char[len + 1];
48     strcpy(str, other.str);
49     return *this;
50 //    return;
51 }
52 
53 int main()
54 {
55     String str1("abc");
56     String str2("123");
57     String str3("456");
58     str1.show();
59     str2.show();
60     str3.show();
61     (str3 = str1) = str2;
62     cout << "str3的内容为:" << endl;
63     str3.show();
64     return 0;
65 }
复制代码

  运行结果:

  

   str3得到了str2的内容,与我们认识的‘=’运算符逻辑相符。

  而如果运算符重载返回的是对象时,

复制代码
 1 //这是返回类型为对象的情况
 2 #include <iostream>
 3 using namespace std;
 4 class String
 5 {
 6 private:
 7     char *str;
 8     int len;
 9 public:
10     String(const char* s);//构造函数声明
11     String operator=(const String& another);//运算符重载,此时返回为空
12     void show()
13     {
14         cout << "value = " << str << endl;
15     }
16 
17     /*copy construct*/
18     String(const String& other)
19     {
20         len = other.len;
21         str = new char[len + 1];
22         strcpy(str, other.str);
23         cout << "copy construct" << endl;
24     }
25 
26     ~String()
27     {
28         delete[] str;
29         cout << "deconstruct" << endl;
30     }
31 };
32 
33 String::String(const char* s)
34 {
35     len = strlen(s);
36     str = new char[len + 1];
37     strcpy(str, s);
38 }
39 
40 String String::operator=(const String &other)
41 {
42     if (this == &other)
43         return *this;
44 //        return;
45     delete[] str;
46     len = other.len;
47     str = new char[len + 1];
48     strcpy(str, other.str);
49     return *this;
50 //    return;
51 }
52 
53 int main()
54 {
55     String str1("abc");
56     String str2("123");
57     String str3("456");
58     str1.show();
59     str2.show();
60     str3.show();
61     (str3 = str1) = str2;
62     cout << "赋值后str3的内容为:" << endl;
63     str3.show();
64     return 0;
65 }
复制代码

 

  运行结果:

  

  str3只得到了str1的内容,并没有得到str2的内容,这是因为执行(str3=str1)后,因为返回的是对象(一个临时对象,str3的一个拷贝),不是引用,所以此时str3不在后面的‘()=str2’的操作中,而是str2对一个临时对象赋值,所以str3的内容保持不变(等于str1)

 

  总结

  所以,对此类运算符重载时,还是老老实实的返回引用,少搞事,做个好男孩:)

<think>嗯,用户想了解C++中如何重载等于运算符,特别是运算符=重载示例。首先,我需要回忆一下C++运算符重载的基本规则。等于运算符通常指的是赋值运算符=和相等判断运算符==,但用户的问题里提到“等于运算符”,可能需要明确区分这两个。不过根据用户后面的示例请求,可能两者都需要涉及。 首先,看看用户提供的引用内容。引用1中提到了-=和+=运算符重载,展示了如何通过成员函数来重载这些运算符返回当前对象的引用。引用2和3则讨论了==和!=重载,特别是引用3中的MyString类重载==运算符,比较了字符串内容和两个整数成员变量。这说明用户可能对如何正确重载这两个运算符感兴趣。 用户的问题中提到“运算符= 重载”,通常赋值运算符=重载是用于对象之间的赋值操作,而==则是用于比较两个对象是否相等。需要确认用户是否混淆了这两个,或者确实想同时了解。根据问题中的关键词“等于运算符”和示例请求,可能用户需要同时了解赋值运算符=和相等运算符==重载,但需要进一步明确。 接下来,我需要组织回答的结构。首先,区分赋值运算符=和相等运算符==,然后分别给出示例。根据引用3中的例子,可以展示如何重载==运算符,而引用1中的例子展示了如何返回*this,这可能适用于赋值运算符重载。 赋值运算符=重载需要注意深拷贝和浅拷贝的问题,特别是在有指针成员的情况下。例如,在MyString类中,szStr是一个指针,进行赋值时需要进行深拷贝,否则会导致内存问题。而相等运算符==则需要比较对象的内容是否相同。 需要确保示例代码正确,符合C++语法,比如运算符重载返回类型、参数类型是否正确。例如,赋值运算符通常返回当前对象的引用,而相等运算符返回bool类型。 用户提供的引用中有关于数组类和MyString类的例子,可以参考这些结构来构建示例。例如,在重载赋值运算符时,需要处理自我赋值的情况,并正确释放原有资源,然后进行深拷贝。 可能还需要提到拷贝构造函数和析构函数的重要性,因为在涉及资源管理(如动态内存)时,这三个函数(拷贝构造、拷贝赋值、析构)通常需要同时实现,称为Rule of Three。 总结回答的结构:首先介绍赋值运算符=和相等运算符==的区别,然后分别给出重载的示例,说明注意事项,如深拷贝、返回类型、参数类型,以及自我赋值检查等。同时,结合用户提供的引用中的代码示例,展示如何在类中实现这些运算符重载。 需要确保回答使用中文,代码示例正确,并符合用户要求的格式,如行内公式使用$...$,独立公式用$$...$$,但在这个问题中可能不需要数学公式,但需要注意LaTeX语法正确。不过用户的问题主要是代码示例,所以重点放在代码的正确性和解释上。 可能生成的相关问题包括:如何区分赋值和相等运算符重载为什么需要处理自我赋值?深拷贝和浅拷贝在运算符重载中的作用?等等。这些问题可以帮助用户进一步理解相关概念。 最后,检查是否有遗漏,比如用户是否还需要其他运算符重载示例,或者关于运算符重载的其他注意事项,如友元函数与成员函数的选择,运算符重载的限制等。但根据用户的问题,集中在等于运算符和赋值运算符上,所以应保持回答的针对性。</think>### C++等于运算符重载详解 在C++中,运算符重载分为两种类型: 1. **赋值运算符 `=`**:用于对象间的赋值操作 2. **相等判断运算符 `==`**:用于比较两个对象是否相等 --- ### 一、赋值运算符 `=` 重载 #### 核心要点 - **必须返回当前对象引用**:`ClassName& operator=()` - **处理自我赋值**:避免资源释放导致错误 - **深拷贝需求**:当类包含指针成员时 #### 示例代码(含指针成员) ```cpp class MyString { private: char* data; int length; public: // 赋值运算符重载 MyString& operator=(const MyString& other) { if (this != &other) { // 自我赋值检查 delete[] data; // 释放旧资源 length = other.length; data = new char[length + 1]; strcpy(data, other.data); } return *this; // 返回当前对象 } }; ``` --- ### 二、相等运算符 `==` 重载 #### 实现方式对比 | 方式 | 成员函数重载 | 友元函数重载 | |-----------|----------------------------------|-----------------------------------| | 语法特点 | 隐含`this`指针 | 需要显式声明两个参数 | | 适用场景 | 对称性要求不高的操作 | 需要支持左右操作数类型转换的场景 | | 访问权限 | 可直接访问私有成员 | 需要类授权`friend`声明 | #### 成员函数实现示例 ```cpp class Vector3D { public: float x, y, z; bool operator==(const Vector3D& other) const { return (x == other.x) && (y == other.y) && (z == other.z); } }; ``` #### 友元函数实现示例 ```cpp class Complex { private: double real, imag; public: friend bool operator==(const Complex& c1, const Complex& c2); }; bool operator==(const Complex& c1, const Complex& c2) { return (c1.real == c2.real) && (c1.imag == c2.imag); } ``` --- ### 三、关键注意事项 1. **三法则(Rule of Three)**:若重载了赋值运算符,应同时实现 - 析构函数 - 拷贝构造函数 - 拷贝赋值运算符 2. **返回类型规范**: - `=` 返回`ClassName&`(支持链式赋值) - `==` 返回`bool` 3. **常量正确性**: ```cpp bool operator==(const MyClass& other) const; // 注意const修饰 ``` --- ### 四、完整类示例(含`=`和`==`) ```cpp class Person { private: char* name; int age; public: // 赋值运算符 Person& operator=(const Person& p) { if (this != &p) { delete[] name; age = p.age; name = new char[strlen(p.name)+1]; strcpy(name, p.name); } return *this; } // 相等运算符(成员函数版) bool operator==(const Person& p) const { return (age == p.age) && (strcmp(name, p.name) == 0); } }; ``` --- ### 性能优化建议 1. **使用移动语义**(C++11+):通过`std::move`优化资源转移 2. **返回值优化**(RVO):利用编译器优化减少拷贝 3. **哈希支持**:重载`==`时建议同时实现`std::hash`特化 ---
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值