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 类的调用运算符,功能就好像一个返回较大值的函数。

 

时间: 2024-10-09 03:04:46

Way on c &amp; c++ 小记 [七] – 重载操作符的相关文章

c++ 重载操作符友元问题

问题描述 c++ 重载操作符友元问题 <c++ primer>里面有一段文字: Salesitem 类也是说明为何有些操作符需要设置为友元的一个好例子.它定义了一个成员操作符,并且有三个非成员操作符.这些非成员操作符需要访问私有数据成员,声明为友元: class Sales_item {friend std::istream& operator>>(std::istream& Sales_item&);friend std::ostream& ope

C++中重载+操作符的正确方法

摘要:本文概要性地介绍如何选择正确的策略来为用户定义类型重载 + 操作符. 用户定义的类型,如:字符串,日期,复数,联合体以及文件常常重载二元 + 操作符以实现对象的连接,附加或合并机制.但是要正确实现 + 操作符会给设计,实现和性能带来一定的挑战.本文将概要性地介绍如何选择正确的策略来为用户定义类型重载这个操作符. 考虑如下的表达式:int x=4+2; 内建的 + 操作符有两个类型相同的操作数,相加并返回右值 6,然后被赋值给 x.我们可以断定内建的 + 是一个二元的,对称的,可交换的操作符

C++ 中重载操作符的设计方法

用户定义的类型,如:字符串,日期,复数,联合体以及文件常常重载二元 + 操作符以实现对象的连接,附加或合并机制.但是要正确实现 + 操作符会给设计,实现和性能带来一定的挑战.本文将概要性地介绍如何选择正确的策略来为用户定义类型重载这个操作符. 考虑如下的表达式: int x=4+2; 内建的 + 操作符有两个类型相同的操作数,相加并返回右值 6,然后被赋值给 x.我们可以断定内建的 + 是一个二元的,对称的,可交换的操作符.它产生的结果的类型与其操作数类型相同.按照这个规测,当你为某个用户定义类

C++重载操作符的设计方法

用户定义的类型,如:字符串,日期,复数,联合体以及文件常常重载二元 + 操作符以实现对象的连接,附加或合并机制.但是要正确实现 + 操作符会给设计,实现和性能带来一定的挑战.本文将概要性地介绍如何选择正确的策略来为用户定义类型重载这个操作符. 考虑如下的表达式: int x=4+2; 内建的 + 操作符有两个类型相同的操作数,相加并返回右值 6,然后被赋值给 x.我们可以断定内建的 + 是一个二元的,对称的,可交换的操作符.它产生的结果的类型与其操作数类型相同.按照这个规测,当你为某个用户定义类

class-模板类重载=操作符

问题描述 模板类重载=操作符 类定义 template <class T> class Element //数据表的元素 { public: T key; //在此处添加除关键码之外的其他数据成员 Element<T>& operator = (Element<T>& x) { key = x.key; return *this; } Element<T>& operator = (T& x) { key = x.key; r

C++编程规范之26:保持重载操作符的自然语义

摘要:     程序员讨厌意外情况:只在有充分理由时才重载操作符,而且应该保持其自然语义:如果做到这一点很困难,那么你可能已经误用了操作符重载.     虽然任何人都会同意不应该在operator+的实现中实现减法操作,但是还有另外一些微妙的情况存在.应该保持操作符正常的语义,这样的程序才能保证别的人员能读懂你的程序,否则你的程序只有你自己能读懂.

重载 操作符 c++-c++重载问题:我在运行后发现重载的后置--操作符没起到应有的作用,求解。

问题描述 c++重载问题:我在运行后发现重载的后置--操作符没起到应有的作用,求解. #include using namespace std; class complex { double real,imag; public: complex(double r=0,double i=0) { real=r; imag=i; } const complex operator--(int) { complex temp(*this); --(*this); return temp; } const

C#操作符重载

11.5.1 问题的提出 在面向对象的程序设计中,自己定义一个类,就等于创建了一个新类型.类的实例和变量一样,可以作为参数传递,也可以作为返回类型. 在第七章中,我们介绍了系统定义的许多操作符.比如对于两个整型变量,使用算术操作符可以简便地进行算术运算: class A { public int x; public int y; public int Plus{ return x+y; } } 再比如,我们希望将属于不同类的两个实例的数据内容相加: class B { public int x;

C#锐利体验之第八讲 索引器与操作符重载

索引 索引器 索引器(Indexer)是C#引入的一个新型的类成员,它使得对象可以像数组那样被方便,直观的引用.索引器非常类似于我们前面讲到的属性,但索引器可以有参数列表,且只能作用在实例对象上,而不能在类上直接作用.下面是典型的索引器的设计,我们在这里忽略了具体的实现. class MyClass{    public object this [int index]    {        get        {            // 取数据        }        set