Way on c & c++ 小记 [七] – 重载操作符

简介:

重载操作符

作者:Jason Lee @http://blog.csdn.net/jasonblog

日期:2010-04-17

 

[1]重载操作符

重载操作符从大的方面来讲可以分为两类:最好或必须作为类的成员函数的,以及相反。而具体地讲,最好或必须作为类的成员函数的有赋值操作符( = )、下标操作符( [] )、调用操作符( () )、成员访问箭头操作符( ->,目前列出的操作符都必须为成员函数) 、星号解引用操作符( * )、复合赋值操作符( +=)、自增、自减。其它的一些操作符,如算术操作符、相等操作符、关系操作符和位操作符,最好定义为非成员函数,在这种情况下,通常需要将其定义为类的友元函数。当然,还有一些是不建议重载的操作符,如逗号、取地址运算符和逻辑运算符等。

 

[2]赋值操作符

赋值操作符必须是类的成员函数,因为编译器需要知道类是否有赋值操作符这个信息。并且,赋值操作符必须返回对*this的引用,也就是左操作数(对象自身)的引用。

同样的,复合赋值操作符也应返回对*this的引用。

如下是一段示例代码:

#include <iostream> using namespace std; class Demo { public: Demo():val(0){} Demo(int t): val(t){} Demo(const Demo &demo){ val = demo.val; } ~Demo(){} Demo& operator=(const Demo &demo){ val = demo.val; return *this; } Demo& operator+=(const Demo &demo){ val += demo.val; return *this; } void showVal(){ cout << val << endl; } private: int val; }; int main(){ Demo d1 = 2;// 首先调用接受整型参数的构造函数创建一个临时对象,再调用复制构造函数 d1.showVal(); Demo d2; d2 += d1;// 使用复合赋值操作符 d2.showVal(); return 0; }

通常定义了赋值操作符,那么接着定义复制构造函数和复合赋值操作符是比较合理的。接着又为了体现复制构造函数的运用,直接在实例化 d1 的时候使用了 Demo d1 = 2; 这样的语句,就类似 string str = “hello”; 先调用对应参数的构造函数创建临时对象再调用复制构造函数。

 

[3] 下标操作符

下标操作符也必须定义为类的成员函数。并且,下标操作符有个需要注意的问题是,当它出现在赋值操作符的任意一边时都应该能正常工作,所以下标操作符应该返回引用,这样才能得到左值,使得下标操作符可以出现在赋值操作符的任意一边。

可以使用下标操作符还保证不非法越界:

#include <iostream> using namespace std; class Demo { public: Demo(): flag(false){} Demo(int sz): flag(true), size(sz){ p = new int[sz]; } ~Demo(){if(flag) delete []p;} int& operator[](const int index){ if(flag){ if(index >= size){/* 非法越界处理代码 */} else return p[index]; } } private: bool flag;// 需要有个标志判断是否有为 p 非配空间,避免非法访存 int *p; int size; }; int main(){ Demo d1(3); d1[0] = 1; int t = d1[0]; return 0; }

 

[4] 箭头和星号操作符

箭头操作符必须定义为类成员函数,而星号操作符则无此要求。有了这两种操作符可以重载,就可以使类表现得像指针一样,或者也可以称其为指针型的类,由此可以实现如 smart pointer 这种虽然号称智能指针但也是智能得有限的类。而 STL 中的迭代器就是一个典型的应用:

#include <iostream> #include <vector> using namespace std; int main () { vector<int> myvector; for (int i=1; i<=5; i++) myvector.push_back(i); vector<int>::iterator it; cout << "myvector contains:"; for ( it=myvector.begin() ; it < myvector.end(); it++ ) cout << " " << *it; cout << endl; return 0; }

 

[5] 算术操作符和关系操作符

算术操作符和关系操作符一般应定义为非成员函数。其中为了保持与内置操作符一致,加法不返回引用。并且,如果可以的话,使用复合赋值操作符来实现算术操作符会更有效率。

相等操作符和不等操作符一般也是相生的,因为需要其一时往往需要另一,并且往往其中一个操作符是调用另一个操作符实现的。

而当使用的容器运作于某些算法需要关系操作符时,如小于操作符,定义该种关系操作符往往会使得代码更加有效率以及简洁。

 

[6] 自增、自减操作符

自增、自减操作符的重载可以使得一个类表现得如整型一般,从而可以作为迭代器,并且又分为前缀和后缀两种运算。

上一段代码(关于星号和箭头操作符)中就有使用到重载自增操作符。通常这种应用是通过 3 个指针来实现的,一个是 begin ,一个是 current ,还有一个是 end 。当然名称不一定如此。首先使用 begin 指针确定迭代开始的初始位置,并使用 end 指针限定范围,最后通过 current 指针遍历元素。

为了与内置类型一致,或者说为了保持习惯用法,前缀式操作符应该返回发生改变(增或减)后的对象的引用,而后缀式操作符应该返回旧值。

另外,为了区分前缀式操作符和后缀式操作符,指定了后缀式操作符函数接收一个无用的 int 型形参,形如 operator++(int) 表示后缀式操作符,而 operator++() 表示前缀式操作符。

 

[7] 输入输出操作符

上面提了许多,但想来最常用的重载操作符可能是输入输出操作符,并且这二者最好定义为非成员函数,使其符合使用标准。

从标准使用的角度来讲,输出操作符应该接受 ostream& 作为第一个形参,并返回对该形参的引用:

#include <iostream> using namespace std; class Demo { public: Demo(): p(0){} ~Demo(){} friend ostream& operator<<(ostream &os, const Demo &demo); private: int p; }; ostream& operator<<(ostream &os, const Demo &demo){ os << demo.p; return os; } int main(){ Demo d1; cout << d1 << endl; return 0; }

而输入操作符也具有相同模式:接受 istream& 参数作为第一形参并返回该形参的引用。此外,输入操作符还需要注意的是读入过程的错误处理。

 

[8] 调用操作符

最后一个提及的重载操作符是调用操作符,它必需作为成员函数,而且因为作用同函数类似,所以具有调用操作符的类经实例化而得的对象也被称为函数对象( function object )。

#include <iostream> using namespace std; class Demo { public: int operator()(int m, int n){ return m>n ? m:n; } }; int main(){ Demo d1; cout << d1(2,3) << endl; return 0; }

如上,使用的是 Demo 类的调用运算符,功能就好像一个返回较大值的函数。

 

目录
相关文章
C++类自加自减与<<运算符的重载实现
C++类自加自减与<<运算符的重载实现
111 0
|
编译器 C++
C++进阶之路:何为运算符重载、赋值运算符重载与前后置++重载(类与对象_中篇)
C++进阶之路:何为运算符重载、赋值运算符重载与前后置++重载(类与对象_中篇)
149 1
C++(十九)new/delete 重载
本文介绍了C++中`operator new/delete`重载的使用方法,并通过示例代码展示了如何自定义内存分配与释放的行为。重载`new`和`delete`可以实现内存的精细控制,而`new[]`和`delete[]`则用于处理数组的内存管理。不当使用可能导致内存泄漏或错误释放。
|
NoSQL 编译器 Redis
c++开发redis module问题之如果Redis加载了多个C++编写的模块,并且它们都重载了operator new,会有什么影响
c++开发redis module问题之如果Redis加载了多个C++编写的模块,并且它们都重载了operator new,会有什么影响
|
编译器 C语言 C++
【C++】基础知识讲解(命名空间、缺省参数、重载、输入输出)
【C++】基础知识讲解(命名空间、缺省参数、重载、输入输出)
112 1
【C++】基础知识讲解(命名空间、缺省参数、重载、输入输出)
|
存储 C++
【C++】string类的使用③(非成员函数重载Non-member function overloads)
这篇文章探讨了C++中`std::string`的`replace`和`swap`函数以及非成员函数重载。`replace`提供了多种方式替换字符串中的部分内容,包括使用字符串、子串、字符、字符数组和填充字符。`swap`函数用于交换两个`string`对象的内容,成员函数版本效率更高。非成员函数重载包括`operator+`实现字符串连接,关系运算符(如`==`, `&lt;`等)用于比较字符串,以及`swap`非成员函数。此外,还介绍了`getline`函数,用于按指定分隔符从输入流中读取字符串。文章强调了非成员函数在特定情况下的作用,并给出了多个示例代码。
|
存储 C++
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
【C/C++学习笔记】string 类型的输入操作符和 getline 函数分别如何处理空白字符
247 0
|
安全 程序员 C++
C++一分钟之-重载运算符
【6月更文挑战第21天】C++的运算符重载让程序员能为自定义类型定制运算符行为,增强代码表达力。但要注意清晰性、优先级和返回类型。遵循运算符原有意义,充分测试,并用注释解释非直观设计。示例展示了如何为复数类重载`+`运算符。避免重载内置类型,注意结合性,且慎用隐式转换。重载应提升可读性而非复杂化代码。
169 2
|
程序员 编译器 C++
c++重载运算符和重载函数
c++重载运算符和重载函数
122 1