Common operators to overload

Most of the work in overloading operators is boiler-plate code. That is little wonder, since operators are merely syntactic sugar, their actual work could be done by (and often is forwarded to) plain functions. But it is important that you get this boiler-plate code right. If you fail, either your operator’s code won’t compile or your users’ code won’t compile or your users’ code will behave surprisingly.

Assignment Operator

There's a lot to be said about assignment. However, most of it has already been said in GMan's famous Copy-And-Swap FAQ, so I'll skip most of it here, only listing the perfect assignment operator for reference:

X& X::operator=(X rhs)    //参数位值传递
{
swap(rhs);
return *this;
}

Bitshift Operators (used for Stream I/O)

The bitshift operators << and >>, although still used in hardware interfacing for the bit-manipulation functions they inherit from C, have become more prevalent as overloaded stream input and output operators in most applications. For guidance overloading as bit-manipulation operators, see the section below on Binary Arithmetic Operators. For implementing your own custom format and parsing logic when your object is used with iostreams, continue.

The stream operators, among the most commonly overloaded operators, are binary infix operators for which the syntax specifies no restriction on whether they should be members or non-members. Since they change their left argument (they alter the stream’s state), they should, according to the rules of thumb, be implemented as members of their left operand’s type. However, their left operands are streams from the standard library, and while most of the stream output and input operators defined by the standard library are indeed defined as members of the stream classes, when you implement output and input operations for your own types, you cannot change the standard library’s stream types. That’s why you need to implement these operators for your own types as non-member functions. The canonical forms of the two are these:

std::ostream& operator<<(std::ostream& os, const T& obj)
{
// write obj to stream
return os;
}
std::istream& operator>>(std::istream& is, T& obj)
{
// read obj from stream
if( /* no valid object of T found in stream */ )
is.setstate(std::ios::failbit);
return is;
}

When implementing operator>>, manually setting the stream’s state is only necessary when the reading itself succeeded, but the result is not what would be expected.

Function call operator

The function call operator, used to create function objects, also known as functors, must be defined as a member function, so it always has the implicit this argument of member functions. Other than this it can be overloaded to take any number of additional arguments, including zero.

Throughout the C++ standard library, function objects are always copied. Your own function objects should therefore be cheap to copy. If a function object absolutely needs to use data which is expensive to copy, it is better to store that data elsewhere and have the function object refer to it.

Comparison operators

The binary infix comparison operators should, according to the rules of thumb, be implemented as non-member functions1. The unary prefix negation ! should (according to the same rules) be implemented as a member function. (but it is usually not a good idea to overload it.)

The standard library’s algorithms (e.g. std::sort()) and types (e.g. std::map) will always only expect operator< to be present. However, the users of your type will expect all the other operators to be present, too, so if you define operator<, be sure to follow the third fundamental rule of operator overloading and also define all the other boolean comparison operators. The canonical way to implement them is this:

inline bool operator==(const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator!=(const X& lhs, const X& rhs){return !operator==(lhs,rhs);}
inline bool operator< (const X& lhs, const X& rhs){ /* do actual comparison */ }
inline bool operator> (const X& lhs, const X& rhs){return operator< (rhs,lhs);}
inline bool operator<=(const X& lhs, const X& rhs){return !operator> (lhs,rhs);}
inline bool operator>=(const X& lhs, const X& rhs){return !operator< (lhs,rhs);}

The important thing to note here is that only two of these operators actually do anything, the others are just forwarding their arguments to either of these two to do the actual work.

The syntax for overloading the remaining binary boolean operators (||&&) follows the rules of the comparison operators. However, it is very unlikely that you would find a reasonable use case for these2.

1 As with all rules of thumb, sometimes there might be reasons to break this one, too. If so, do not forget that the left-hand operand of the binary comparison operators, which for member functions will be *this, needs to be const, too. So a comparison operator implemented as a member function would have to have this signature:

bool operator<(const X& rhs) const { /* do actual comparison with *this */ }

(Note the const at the end.)

2 It should be noted that the built-in version of || and && use shortcut semantics. While the user defined ones (because they are syntactic sugar for method calls) do not use shortcut semantics. User will expect these operators to have shortcut semantics, and their code may depend on it, Therefore it is highly advised NEVER to define them.

Arithmetic Operators

Unary arithmetic operators

The unary increment and decrement operators come in both prefix and postfix flavor. To tell one from the other, the postfix variants take an additional dummy int argument. If you overload increment or decrement, be sure to always implement both prefix and postfix versions. Here is the canonical implementation of increment, decrement follows the same rules:

class X {
X& operator++()
{
// do actual increment
return *this;
}
X operator++(int) //后缀运算符,返回值为 值传递
{
X tmp(*this);
operator++();
return tmp;
}
};

Note that the postfix variant is implemented in terms of prefix. Also note that postfix does an extra copy.2

Overloading unary minus and plus is not very common and probably best avoided. If needed, they should probably be overloaded as member functions.

2 Also note that the postfix variant does more work and is therefore less efficient to use than the prefix variant. This is a good reason to generally prefer prefix increment over postfix increment. While compilers can usually optimize away the additional work of postfix increment for built-in types, they might not be able to do the same for user-defined types (which could be something as innocently looking as a list iterator). Once you got used to do i++, it becomes very hard to remember to do ++i instead when i is not of a built-in type (plus you'd have to change code when changing a type), so it is better to make a habit of always using prefix increment, unless postfix is explicitly needed.

Binary arithmetic operators

For the binary arithmetic operators, do not forget to obey the third basic rule operator overloading: If you provide +, also provide +=, if you provide -, do not omit -=, etc. Andrew Koenig is said to have been the first to observe that the compound assignment operators can be used as a base for their non-compound counterparts. That is, operator + is implemented in terms of +=- is implemented in terms of -= etc.

According to our rules of thumb, + and its companions should be non-members, while their compound assignment counterparts (+= etc.), changing their left argument, should be a member. Here is the exemplary code for += and +, the other binary arithmetic operators should be implemented in the same way:

class X {
X& operator+=(const X& rhs)
{
// actual addition of rhs to *this
return *this;
}
};
inline X operator+(X lhs, const X& rhs)
{
lhs += rhs;
return lhs;
}

operator+= returns its result per reference, while operator+ returns a copy of its result. Of course, returning a reference is usually more efficient than returning a copy, but in the case of operator+, there is no way around the copying. When you write a + b, you expect the result to be a new value, which is why operator+ has to return a new value.3 Also note that operator+ takes its left operand by copy rather than by const reference. The reason for this is the same as the reason giving for operator= taking its argument per copy.

The bit manipulation operators ~ & | ^ << >> should be implemented in the same way as the arithmetic operators. However, (except for overloading << and >> for output and input) there are very few reasonable use cases for overloading these.

3 Again, the lesson to be taken from this is that a += b is, in general, more efficient than a + b and should be preferred if possible.

Array Subscripting

The array subscript operator is a binary operator which must be implemented as a class member. It is used for container-like types that allow access to their data elements by a key. The canonical form of providing these is this:

class X {
value_type& operator[](index_type idx);
const value_type& operator[](index_type idx) const;
// ...
};

Unless you do not want users of your class to be able to change data elements returned by operator[] (in which case you can omit the non-const variant), you should always provide both variants of the operator.

If value_type is known to refer to a built-in type, the const variant of the operator should return a copy instead of a const reference.

Operators for Pointer-like Types

For defining your own iterators or smart pointers, you have to overload the unary prefix dereference operator * and the binary infix pointer member access operator ->:

class my_ptr {
value_type& operator*();
const value_type& operator*() const;
value_type* operator->();
const value_type* operator->() const;
};

Note that these, too, will almost always need both a const and a non-const version. For the ->operator value_type must be of class (or struct or union) type, otherwise their implementation results in a compile-time error.

The unary address-of operator should never be overloaded.

For operator->*() see this question. It's rarely used and thus rarely ever overloaded. In fact, even iterators do not overload it.

---------------------------------------------------------------------------

-摘自stackoverflow http://stackoverflow.com/questions/4421706/operator-overloading/4421708#4421708

Common operators to overload-c++运算符重载的标准语法(全)的更多相关文章

  1. C++:运算符重载函数之成员运算符重载函数

    5.2.3 成员运算符重载函数 在C++中可以把运算符重载函数定义为某个类的成员函数,称之为成员运算符重载函数. 1. 定义成员运算符重载函数的语法形式 (1)在类的内部,定义成员运算符重载函数的格式 ...

  2. C++:运算符重载函数之友元运算符重载

    5.2.2 友元运算符重载函数 运算符重载函数一般采用两种形式定义: 一是定义为它将要操作的类的成员函数(简称运算符重载函数): 二是定义为类的友元函数(简称为友元运算符重载函数). 1.定义友元运算 ...

  3. 探索Scala(1)-- 运算符重载

    Scala语言运算符重载全然是语法层面的小把戏,本文记录我对Scala语言运算符重载的一些理解. 方法调用语法糖 调用方法时,Scala同意省略点号和圆括号,如以下代码所看到的: 把运算符映射成单词 ...

  4. C++学习6-面向对象编程基础(运算符重载、类的派生与继承、命名空间)

    运算符重载 重载的运算符是具有特殊名字的函数:它们的名字由关键字operator和其后要定义的运算符号共同组成.重载的运算符是遵循函数重载的选择原则,根据不同类型或不同参数来选择不同的重载运算符. 运 ...

  5. C++运算符重载的妙用

    运算符重载(Operator overloading)是C++重要特性之中的一个,本文通过列举标准库中的运算符重载实例,展示运算符重载在C++里的妙用.详细包含重载operator<<,o ...

  6. Java 原始数据类型的计算:运算符重载(Operator Overload)和类型转换(Type Conversion)

    原文阅读:<算法(第四版)>第一章 第一节:基础编程模型 有没有在面试的时候被问到:下面这几行代码的执行结果是什么?依据是什么? System.out.println (5/3); Sys ...

  7. C++运算符重载

    C++运算符重载 基本知识 重载的运算符是具有特殊名字的函数,他们的名字由关键字operator和其后要定义的运算符号共同组成. 运算符可以重载为成员函数和非成员函数.当一个重载的运算符是成员函数时, ...

  8. C++运算符重载——重载特殊运算符

    1.重载赋值运算符= 赋值运算符用于同类对象间的相互赋值.赋值运算符只能被重载为类的非静态成员函数,不能重载为友元函数和普通函数. 对于用户自定义的类而言,如果没有重载赋值运算符,那么C++编译器会为 ...

  9. C++运算符重载——重载二元运算符

    1.重载二元操作符的方法 二元运算符又称为双目运算符,即需要2个操作数的运算符,例如 + - * / 等. 运算符重载可以分为3种方式:类的非静态成员函数.类的友元函数.普通函数. 例如有 2 个操作 ...

随机推荐

  1. VisualGDB系列10:快速调试Linux应用程序

    根据VisualGDB官网(https://visualgdb.com)的帮助文档大致翻译而成.主要是作为个人学习记录.有错误的地方,Robin欢迎大家指正. 本文介绍如何快速调试GCC构建的Linu ...

  2. leetcode Single Number II - 位运算处理数组中的数

    题目描述: 给定一个包含n个整数的数组,除了一个数出现一次外所有的整数均出现三次,找出这个只出现一次的整数. 题目来源: http://oj.leetcode.com/problems/single- ...

  3. rsync 简单使用 非默认ssh端口 分别从远程获取及推送本地的文件到远程

    rsync: did not see server greetingrsync error: error starting client-server protocol (code 5) at mai ...

  4. 【Android 多媒体应用】使用MediaCodec解码使用SurfaceView显示视频

    1.MainActivity.java import android.app.Activity; import android.os.Bundle; import android.os.Environ ...

  5. opencv 美白磨皮人脸检测<转>

    1. 简介 这学期的计算机视觉课,我们组的课程项目为“照片自动美化”,其中我负责的模块为人脸检测与自动磨皮.功能为:用户上传一张照片,自动检测并定位出照片中的人脸,将照片中所有的人脸进行“磨皮”处理, ...

  6. Django的Model使用

    创建模型 使用Django的模型主要注意两个方面:字段的类型和方法的重写.这里用一个例子来说明,其中包含了常用的字段类型和如何重写方法. from django.db import models cl ...

  7. 巧用cssText属性

    给一个HTML元素设置css属性,如 1 2 3 4 var head= document.getElementById("head"); head.style.width = & ...

  8. [xdoj1007]易碎的鸟蛋(dp)

    解题思路:f[n,m]表示n层楼.m个鸡蛋时所需要的最小次数,则 转移方程为:f[n,m] = min{ 1+max(f[i-1,m-1], f[n-i,m]) | i=1..n }初始条件:f[i, ...

  9. 【总结整理】JS的继承

    参考阮一峰的文章:http://javascript.ruanyifeng.com/oop/inheritance.html#toc4 function Shape() { this.x = 0; t ...

  10. html标签的显示模式(块级标签,行内标签,行内块标签)(转)

    html标签的显示模式(块级标签,行内标签,行内块标签)   今天讲课的时候,讲到了html中的标签的显示模式,大致分为块级标签和行内标签.那么初学者在刚使用标签的时候会发现有些属性在一些标签上不起作 ...