[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 


==

Ref: C++ 关系运算符重载

声明关键字 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(10); person p2(20); if(p1==p2) cout<<”the age is equal!”< return 0; }

 

 

>>,  <<,  +,  +=

包括:(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 = 1;
    this->b = 1;
}

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{1, 2}; Test t2{10, 20}; /* * 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{3}, SmallInt{1}, SmallInt{2}}; sort(vec.begin(), vec.end()); copy(vec.begin(), vec.end(), std::ostream_iterator<SmallInt>(std::cout, " ")); return 0; }

 

加法运算符重载 de 返回值

函数直接返回类,以为着什么?

会直接调用拷贝构造函数,然后默认实施的是:逐位拷贝语义.

Ref: C++进阶系列:拷贝构造函数与NRV优化

Ref: 关于NRV优化[例子非常不粗]

请问从a, b传入函数开始,一共创建了多少个对象?

Vector a, b;
Vector c = add(a, b);

 

操作符重载 与 友元函数

C++操作符重载形式——成员函数or友元函数

一般来说,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 = 0;
    Sum_t s(&total);  // <-- 这个是构造参数

    int arr[] = {0, 1, 2, 3, 4, 5}; 
 
    std::for_each(arr, arr+6, 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 = 0; 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{10};
//    s[12] = 2;    // 这算是两个operator,因为是有赋值运算,必须返回&类型.
    cout << s[2] << 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 = 0; minutes = 0; } Time(int h, int m){ hours = h; minutes = m; }
// 显示时间的方法 void displayTime() { cout << "H: " << hours << " M:" << minutes <<endl; }
// 重载前缀递增运算符( ++x ) Time operator ++ () { ++minutes; // 对象加 1 if(minutes >= 60) { ++hours; minutes -= 60; } return Time(hours, minutes); }
// 重载后缀递增运算符( x++ ) Time operator ++ (int) { // 保存原始值 Time T(hours, minutes); // 对象加 1 ++minutes; if(minutes >= 60) { ++hours; minutes -= 60; } // 返回旧的原始值 return T; } };

-------------------------------------------------------------
int main() { Time T1(11, 59), T2(10,40); ++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 0; }

 

 

指针相关的,比较复杂,但不经常用到的运算符重载.

 

  

指针运算符 -> , *

/* 感觉用处不是很大 */

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 = 10;
int Obj::j = 12;
 
// 为上面的类实现一个容器
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 = 0;
   }
   // 返回值表示列表结束
   bool operator++() // 前缀版本
   { 
     if(index >= oc.a.size() - 1) return false;
     if(oc.a[++index] == 0) return false;
     return true;
   }
   bool operator++(int) // 后缀版本
   { 
      return operator++();
   }
   // 重载运算符 ->
   Obj* operator->() const 
   {
     if(!oc.a[index])
     {
        cout << "Zero value";
        return (Obj*)0;
     }
     return oc.a[index];
   }
};
 
int main() {
   const int sz = 10;
   Obj o[sz];
   ObjContainer oc;
   for(int i = 0; i < sz; i++)
   {
       oc.add(&o[i]);
   }
   SmartPointer sp(oc); // 创建一个迭代器
   do {
      sp->f(); // 智能指针调用
      sp->g();
   } while(sp++);
   return 0;
}
View Code

 

类成员访问运算符( -> )可以被重载,但它较为麻烦。它被定义用于为一个类赋予"指针"行为。运算符 -> 必须是一个成员函数。如果使用了 -> 运算符,返回类型必须是指针或者是类的对象。

运算符 -> 通常与指针引用运算符 * 结合使用,用于实现"智能指针"的功能。这些指针是行为与正常指针相似的对象,唯一不同的是,当您通过指针访问对象时,它们会执行其他的任务。比如,当指针销毁时,或者当指针指向另一个对象时,会自动删除对象。

-> 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.

posted @ 2016-12-15 09:32  郝壹贰叁  阅读(2458)  评论(0编辑  收藏  举报