[c++] Operator overloading
Introduction
一、函数重载
关于重载 Overloading,最基本的是根据以下两个特性:
- 基于参数
- 基于const
其实,函数重载也没啥多余值得说的东西。
二、自定义操作规则
c++的操蛋属性:自己为一档,空一档,其他随意。
UB_stack a;
UB_stack b = a; // copy
auto c = a;
auto d {a}; // (or auto d = {a}), deduced type is std::initializer_list
这是一个抓狂的问题,详见:http://scottmeyers.blogspot.com.au/2014/03/if-braced-initializers-have-no-type-why.html
Goto: C++11:std::initializer_list
大神的无奈
今日一乐:为何感觉到了Scott对chinese edition是黑白版本的好奇和无奈。
三、可重载 or 不可重载
Goto: C++ 重载运算符和重载函数
下面是:可重载的运算符列表
双目算术运算符 | + (加),-(减),*(乘),/(除),% (取模) |
关系运算符 | ==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于) |
逻辑运算符 | ||(逻辑或),&&(逻辑与),!(逻辑非) |
单目运算符 | + (正),-(负),*(指针),&(取地址) |
自增自减运算符 | ++(自增),--(自减) |
位运算符 | | (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移) |
赋值运算符 | =, +=, -=, *=, /= , % = , &=, |=, ^=, <<=, >>= |
空间申请与释放 | new, delete, new[ ] , delete[] |
其他运算符 | ()(函数调用),->(成员访问),,(逗号),[](下标) |
下面是:不可重载的运算符列表
成员访问运算符 | . |
成员指针访问运算符 | .*, ->* |
域运算符 | :: |
长度运算符 | sizeof |
条件运算符 | ? |
预处理符号 | # |
Overloaded Operator
==
声明关键字 operator,以及紧跟其后的一个c++预定义的操作符,举例如下:
// 申明关键字
class person{
private:
int age;
public:
person(int a){
this->age=a;
}
inline bool operator == (const person &ps) const;
}; // 实现方式如下
inline bool person::operator == (const person &ps) const
{
if (this->age==ps.age) // 这里的this看上去是“符号”左边的类
return true;
return false;
} int main()
{
person p1();
person p2();
if(p1==p2) cout<<”the age is equal!”< return ;
}
>>, <<, +, +=
包括:(1) 输入输出;(2) 自增自减;(3) 运算.
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <UB_stack.h> using namespace std; class Test {
public:
Test(int x, int y):a{x},b{y}{} // The output operator must be defined as a friend function
// and is usually a non-member function.
// The input operator is similar.
friend ostream& operator << (ostream&, const Test &);
friend istream& operator >> (istream&, Test&);
friend Test operator +(const Test&, const Test&);// Usually implemented as a member function.
Test& operator += (const Test &); int returnA(void);
void init(void); private:
int a;
int b;
}; /******************************************************************************/ void Test::init(void)
{
this->a = ;
this->b = ;
} int Test::returnA(void)
{
return (this->a);
} ostream& operator << (ostream &os, const Test &t)
{
os << t.a << " " << t.b << endl;
} istream& operator >> (istream &is, Test &t)
{
is >> t.a >> t.b;
} Test& Test::operator += (const Test &t)
{
this->a += t.a;
this->b += t.b; return *this;
} Test operator + (const Test &t1, const Test &t2)
{
Test ret = t1;
ret += t2;
return ret;
} /******************************************************************************/ class SmallInt {
public:
friend ostream& operator << (ostream &os, const SmallInt &s);
friend bool operator < (const SmallInt&, const SmallInt&);
SmallInt(int v): value_{v} {}
private:
int value_;
};
// friend function.
bool operator < (const SmallInt &rhs,const SmallInt &lhs) {
return rhs.value_ <= lhs.value_;
}
// friend function
std::ostream& operator<<(std::ostream &os, const SmallInt &s) {
os << s.value_;
return os;
} /******************************************************************************/ int main()
{
cout << "Hello World!" << endl; Test t1{, };
Test t2{, }; /*
* I/O Operators
*/
cout << t1 << t2;
cin >> t2;
cout << t1 << t2; t1.init();
t2.init(); /*
* Compound Assignment Operators
*/
t2 += t1;
cout << t2; /*
* Arithmetic Operators
*/
cout << t1+t2; /*
* Relational Operators
* ...
*/ /*
* Using the STL Sort and Copy Algorithms
*/
vector<SmallInt> vec{SmallInt{}, SmallInt{}, SmallInt{}}; sort(vec.begin(), vec.end());
copy(vec.begin(), vec.end(), std::ostream_iterator<SmallInt>(std::cout, " ")); return ;
}
加法运算符重载 de 返回值
函数直接返回类,以为着什么?
会直接调用拷贝构造函数,然后默认实施的是:逐位拷贝语义.
Ref: C++进阶系列:拷贝构造函数与NRV优化
Ref: 关于NRV优化[例子非常不粗]
请问从a, b传入函数开始,一共创建了多少个对象?
Vector a, b;
Vector c = add(a, b);
操作符重载 与 友元函数
一般来说,C++运算符重载可采用成员函数和友元函数,二者都可以访问类的私有成员,那么该采用哪一种呢?
(1)当重载为成员函数时,会隐含一个this指针;当重载为友元函数时,不存在隐含的this指针,需要在参数列表中显示地添加操作数。
上述的代码中,因为用了fridend函数,因此没有用this,所以一元运算符重载 "用到了两个参数".
(2)当重载为成员函数时,只允许右参数的隐式转换;当重载为友元函数时,能够接受左参数和右参数的隐式转换。
如果采用成员函数形式CString::operator+(const CString& rhs),则只能接受CString+char;
如果执行char+CString则会编译出错。
简单类型可以"隐式转换"为复杂类型.
class CString
{
public:
CString(char* str);
private:
char* m_pStr;
};
一般而言,对于双目运算符,最好将其重载为友元函数;而对于单目运算符,则最好重载为成员函数。
但是也存在例外情况。有些双目运算符是不能重载为友元函数的,比如 赋值运算符=、函数调用运算符()、下标运算符[]、指针运算符-> 等,因为这些运算符在语义上与this都有太多的关联。
比如=表示“将自身赋值为…”,[]表示“自己的第几个元素”,如果将其重载为友元函数,则会出现语义上的不一致。
赋值运算符 =
实际操作当中,调用的是:拷贝构造函数。
返回void,只是”赋值“的简单版本:https://www.runoob.com/cplusplus/assignment-operators-overloading.html
函数调用运算符 ()
也叫做:functor
#include <stdio.h>
#include <string.h>
#include <thread>
#include <functional>
#include <iostream>
#include <stdio.h>
#include <algorithm> using namespace std; struct Sum_t
{
Sum_t(int * t):total(t)
{}; int * total; void operator () (int element)
{
*total+=element;
}
}; int main()
{
int total = ;
Sum_t s(&total); // <-- 这个是构造参数 int arr[] = {, , , , , }; std::for_each(arr, arr+, s); // <-- 类作为函数来使用
cout << total << endl;
}
下标运算符 []
假设 X 是某一个类的对象,类中定义了重载“[ ]”的 operator[ ] 函数,则表达式:
X[Y]; 可被解释为: X.operator[](Y);
定义实例:
class SafeArray {
public:
SafeArray(int s);
SafeArray(const int v[], int s);
~SafeArray() {delete[] values;} int& operator [] (int i);
int operator [] (int i) const; private:
int size;
int *values;
};
// 构造函数的实现
SafeArray::SafeArray(int s) : size{s}, values{new int[size]} {}
SafeArray::SafeArray(const int v[], int s) : size{s}
{
values = new int[size];
for (int i = ; i < size; i++)
{
values[i] = v[i];
}
}
// 符号重载的实现
int& SafeArray::operator [](int index) {
assert((index >= 0) && (index < size));
return values[index];
} int SafeArray::operator [](int index) const { // 常函数不能修改 函数内的成员,不能用于右赋值.
assert((index >= 0) && (index < size));
return values[index];
}
使用样例:
SafeArray s{};
// s[12] = 2; // 这算是两个operator,因为是有赋值运算,必须返回&类型.
cout << s[] << endl;
自增自减符 ++, --
后缀法加了一个参数,有点意思。
From: https://www.runoob.com/cplusplus/increment-decrement-operators-overloading.html
#include <iostream>
using namespace std; class Time
{
private:
int hours; // 0 到 23
int minutes; // 0 到 59
public:
// 所需的构造函数
Time(){
hours = ;
minutes = ;
}
Time(int h, int m){
hours = h;
minutes = m;
}
// 显示时间的方法
void displayTime()
{
cout << "H: " << hours << " M:" << minutes <<endl;
}
// 重载前缀递增运算符( ++x )
Time operator ++ ()
{
++minutes; // 对象加 1
if(minutes >= )
{
++hours;
minutes -= ;
}
return Time(hours, minutes);
}
// 重载后缀递增运算符( x++ )
Time operator ++ (int)
{
// 保存原始值
Time T(hours, minutes);
// 对象加 1
++minutes;
if(minutes >= )
{
++hours;
minutes -= ;
}
// 返回旧的原始值
return T;
}
}; -------------------------------------------------------------
int main()
{
Time T1(, ), T2(,); ++T1; // T1 加 1
T1.displayTime(); // 显示 T1
++T1; // T1 再加 1
T1.displayTime(); // 显示 T1 T2++; // T2 加 1
T2.displayTime(); // 显示 T2
T2++; // T2 再加 1
T2.displayTime(); // 显示 T2
return ;
}
指针相关的,比较复杂,但不经常用到的运算符重载.
指针运算符 -> , *
/* 感觉用处不是很大 */
Ref: C++ 类成员访问运算符 -> 重载
#include <iostream>
#include <vector>
using namespace std; // 假设一个实际的类
class Obj {
static int i, j;
public:
void f() const { cout << i++ << endl; }
void g() const { cout << j++ << endl; }
}; // 静态成员定义
int Obj::i = ;
int Obj::j = ; // 为上面的类实现一个容器
class ObjContainer {
vector<Obj*> a;
public:
void add(Obj* obj)
{
a.push_back(obj); // 调用向量的标准方法
}
friend class SmartPointer;
}; // 实现智能指针,用于访问类 Obj 的成员
class SmartPointer {
ObjContainer oc;
int index;
public:
SmartPointer(ObjContainer& objc)
{
oc = objc;
index = ;
}
// 返回值表示列表结束
bool operator++() // 前缀版本
{
if(index >= oc.a.size() - ) return false;
if(oc.a[++index] == ) return false;
return true;
}
bool operator++(int) // 后缀版本
{
return operator++();
}
// 重载运算符 ->
Obj* operator->() const
{
if(!oc.a[index])
{
cout << "Zero value";
return (Obj*);
}
return oc.a[index];
}
}; int main() {
const int sz = ;
Obj o[sz];
ObjContainer oc;
for(int i = ; i < sz; i++)
{
oc.add(&o[i]);
}
SmartPointer sp(oc); // 创建一个迭代器
do {
sp->f(); // 智能指针调用
sp->g();
} while(sp++);
return ;
}
类成员访问运算符( -> )可以被重载,但它较为麻烦。它被定义用于为一个类赋予"指针"行为。运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。
运算符 -> 通常与指针引用运算符 * 结合使用,用于实现"智能指针"的功能。这些指针是行为与正常指针相似的对象,唯一不同的是,当您通过指针访问对象时,它们会执行其他的任务。比如,当指针销毁时,或者当指针指向另一个对象时,会自动删除对象。
-> must be a member function and * is usually a member.
(Here 简介)
貌似不错的智能指针的博文:http://www.cnblogs.com/lanxuezaipiao/p/4132096.html
The Basic Idea Behind All Smart Pointers
类型转换操作符
Type Conversion Operators
Conversion operators must be defined as member functions. They
do not take any parameters, nor do they specify a return type.
Typically conversions don’t modify the object and are declared
const.
如果去掉explicit(显式的),则User code中的line 2即可成立。
explicit: 声明为explicit的构造函数,不能在隐式转换中使用。
End.
[c++] Operator overloading的更多相关文章
- Operator overloading
By defining other special methods, you can specify the behavior of operators on user-defined types. ...
- [置顶] operator overloading(操作符重载,运算符重载)运算符重载,浅拷贝(logical copy) ,vs, 深拷贝(physical copy)
operator overloading(操作符重载,运算符重载) 所谓重载就是重新赋予新的意义,之前我们已经学过函数重载,函数重载的要求是函数名相同,函数的参数列表不同(个数或者参数类型).操作符重 ...
- Lintcode208 Assignment Operator Overloading (C++ Only) solution 题解
[题目描述] Implement an assignment operator overloading method. Make sure that: The new data can be copi ...
- C# to IL 5 Operator Overloading(操作符重载)
Every operator overload that we use in C#, gets converted to a function call in IL. Theoverloaded &g ...
- 重载操作符 operator overloading 学习笔记
重载操作符,只是另外一种调用函数的方法和表现方式,在某些情况它可以让代码更简单易读.注意不要过度使用重载操作符,除非它让你的类更简单,让你的代码更易读. 1语法 如下: 其中友元,关键字不是必须的,但 ...
- 面向对象程序设计-C++ Operator Overloading & Type conversion (Static)【第十一次上课笔记】
本次上课继续讲解了 [ ] .-> 等运算符重载的具体例子 也讲解了C++单个参数的类的类型转换的案例 最后稍微提到了 static 的第三种作用:静态数据成员 具体详解我都已注释出来了,大家可 ...
- 面向对象程序设计-C++ Default constructor & Copy constructor& Destructor & Operator Overloading【第九次上课笔记】
先上笔记内容吧: 这次上课的内容有关 构造函数 析构函数 运算符重载 return * this 内容很细,大家好好回顾笔记再照应程序复习吧 :) #include <iostream> ...
- 8 Operator overloading
在类中,Groovy支持你使用标准的操作符.例如,如果你想使用a+b操作(a和b来自于Z类),那么你在Z类中,必须实现(implement)plus(Zname)方法.
- c++ operator
这篇博文是以前很久写的,贴在我的早期一个blog中,今天google一下,发现还真有不少人转载,可惜并不注明出处.那时觉得operator比较好玩.C++有时它的确是个耐玩的东东.operator它有 ...
随机推荐
- cf 红名计划!
我要成为红名爷! 这是现在好弱好弱的窝 >_< ****************UPD ON 2015/12/10 0:20 啊啊啊啊啊啊啊啊啊啊啊啊把时间记错了啊QAQ 我也不知道为什么 ...
- ios设备中openGL所支持的最大纹理尺寸
这几天碰到一个在iphone4上显示图片未黑色矩形的bug,在其他机器上都正常 最后发现是图片打包尺寸的关系,iphone4无法读取2048以上大小的单个图片,所以其中的图片都显示成了黑色,希望对碰到 ...
- Python之路Day15--CSS补充以及JavaScript(一)
一.上节作业问题: 上节作业问题: 1.css重用 <style> 如果整个页面的宽度 > 900px时: { .c{ 共有 } .c1{ 独有 } } .c2{ 独有 } < ...
- Python之路第一课Day7--随堂笔记(面向对象编程进阶...未完待续 )
本节内容: 面向对象高级语法部分 静态方法.类方法.属性方法 类的特殊方法 反射 异常处理 Socket开发基础 作业:开发一个支持多用户在线的FTP程序 面向对象高级语法部分 一.静态方法 通过@s ...
- centos下python中添加easygui模块
前提:python中要集成Tkinter,Tkinter模块("Tk 接口")是Python的标准Tk GUI工具包的接口.Tk和Tkinter可以在大多数的Unix平台下使用,同 ...
- SVN Server for Migration
SVN Server: http://mxsuse01/svn/repos/erp/Oracle_EMS Report SVN (Put to SVN Sort) 1. *.RDF 2. *CP.LD ...
- 封装自己的DB类(PHP)
封装一个DB类,用来专门操作数据库,以后凡是对数据库的操作,都由DB类的对象来实现.这样有了自己的DB类,写项目时简单的sql语句就不用每次写了,直接调用就行,很方便! 1.封装一个DB类.一个类文件 ...
- HTML Help WorkShop 创作、调用方法和技巧
一.在CHM 文件中所用到的弹出式菜单: 二.在HTML Help WorkShop 中不用显示的样式 (1).从右到右阅读顺序. (2).对话框 (3).滚动条在左边 (4).仅展开单个标题 ...
- windows上面捕获声卡数据
转自:http://shanewfx.github.io/blog/2013/08/14/caprure-audio-on-windows/ 前一段时间接到一个任务,需要采集到声卡的输出信号,以便与麦 ...
- 春节前最后一篇,CRUD码农专用福利:PDF.NET之SOD Version 5.1.0 开源发布(兼更名)
废话不多说,直接入正题,明天赶着坐火车回老家过年. 从2013.10.1日起,原PDF.NET将更名为 SOD :- one SQL-MAP,ORM,Data Control framework 原P ...