1.对于二元运算符来说,左侧运算对象传递给第一个参数,而右侧运算对象传递给第二个参数。除了重载的函数调用运算符operator()之外,其他重载元素运算符不能含有默认实参。

  1. class test
  2. {
  3. void operator() (int i = ) {}; // 正确, 但是operator()只能是成员函数
  4. };
  5.  
  6. int operator+ (test t, int j = ); // 错误,不可以有默认实参

2.当一个重载的运算符是成员函数时,this绑定到左侧运算对象。成员运算符函数的(显示)参数数量比运算对象的数量少一个。

  1. class test
  2. {
  3. public:
  4. int operator+ (int i) { return i + ; };
  5. };
  6.  
  7. test t;
  8. int i = t + ;

3.对一个运算符函数来说,它或是类的成员,或者至少含有一个类类型或枚举类型的参数,所以我们无法改变内置类型的运算符的含义。

  1. class test
  2. {
  3. };
  4.  
  5. int operator- (int i, test t); // 正确
  6. int operator+ (int i, int j); // 错误,没有类类型或枚举的参数

4.以下是可重载和不可重载运算符的列表:

  • 有四个符号(+,-,*,&)既是一元运算符,也是二元运算符,从参数的数量我们可以推断到底定义的是哪种运算符。
  • 对于一个重载的运算符来说,其优先级和结合律与内置运算符保持一致。
  • 赋值(=),下标([ ]),调用(( ))和成员访问箭头(->)运算符必须是成员

5.重载运算符函数的调用方法:

  1. class test
  2. {
  3. public:
  4. int operator+=(int i) { return + i; }
  5. };
  6.  
  7. int operator- (int i, test t)
  8. {
  9. return i - ;
  10. }
  11.  
  12. test t;
  13. // 以下两种调用是等价的
  14. int i = - t;
  15. i = operator-(, t);
  16.  
  17. // 以下两种调用是等价的
  18. i = t += ;
  19. i = t.operator+=();

6.与iostream标准库兼容的输入输出运算符必须是普通的非成员函数,而不能是类的成员函数。否则,它的左侧运算对象将是我们的类的一个对象。

7.operator<<一般要返回它的ostream形参,operator>>一般要返回它的istream形参,输入运算符必须处理输入可能失败的情况。

8.定义递增和递减运算符的类应该同时定义前置版本和后置版本。为了与内置版本保持一致,前置运算符应该返回递增或递减后对象的引用。后置运算符应该返回对象的原值,返回的形式是一个值而非引用。为了区分前置和后置运算符,后置版本接受一个额外的(不被使用)int型形参,当我们使用后置运算符的时候,编译器为这个形参提供一个值为0的实参,尽管从语法上来说后置函数可以使用这个额外的形参,但是实际过程中我们通常不会这么做。

  1. class test
  2. {
  3. public:
  4. test(int i) :m_count(i) {}
  5.  
  6. test& operator++() // 前置运算符
  7. {
  8. ++m_count;
  9. return *this;
  10. }
  11.  
  12. test operator--(int) // 后置运算符
  13. {
  14. test tmp = *this;
  15. --m_count;
  16. return tmp;
  17. }
  18.  
  19. int m_count;
  20. };
  21.  
  22. test t();
  23. test t1 = ++t; // t1.m_count = 2
  24. test t2 = t--; // t2.m_count = 2
  25.  
  26. // 显示调用
  27. test t3 = t.operator++(); // t3.m_count = 2
  28. test t4 = t.operator--(); // t4.m_count = 2

9.我们能令operator*完成任何我们指定的操作,比如返回一个固定的值等等,但是operator->获取成员的事实则永远不变,我们可以改变箭头是从哪个对象中获取成员,例如如果箭头的对象是string类型那么箭头获取的成员一定有size(),这个是无法改变的。重载的箭头运算必须返回类的指针或者自定义了箭头运算符的某个类的对象。

  1. class test
  2. {
  3. public:
  4. test(std::string name) :m_name(name) {}
  5.  
  6. std::string* operator->()
  7. {
  8. return &(this->m_name);
  9. }
  10.  
  11. std::string m_name;
  12. };
  13.  
  14. test t("test");
  15. std::size_t i = t->size(); // i=4

10.函数调用运算符必须是成员函数,一个类可以定义多个不同版本的调用运算符,互相之间应该在参数或类型上有所区别。如果类定义了调用运算符,则该类的对象称作函数对象。

  1. class test
  2. {
  3. public:
  4. int operator()(int i)
  5. {
  6. return i > ? i : -i;
  7. }
  8.  
  9. };
  10.  
  11. test abs;
  12. int i = abs(-); // i=1

11.标准库定义了一组表示算术运算符,关系运算符和逻辑运算符的类,每个类分别定义了一个执行命名操作的调用运算符。这些类都被定义成模板的形式。

  1. std::plus<int> test;
  2. int i = test(, ); // i=30

12.我们可以将标准库函数对象运用在算法中,而且标准库规定其函数对象对于指针同样适用。

  1. std::vector<std::string> vec = { "","","" };
  2. std::sort(vec.begin(), vec.end(), std::greater<std::string>()); // 将默认的升序排列改为了降序排列
  3.  
  4. std::vector<std::string*> vecp = { new std::string(""), new std::string(""), new std::string("") };
  5. // 错误,以下比较指针的大小是未定义的
  6. std::sort(vecp.begin(), vecp.end(), [](std::string *a, std::string *b) { return a < b; });
  7.  
  8. // 正确,标准库规定指针的less是定义良好的
  9. std::sort(vecp.begin(), vecp.end(), std::less<std::string*>());

13.我们可以使用一个名为function的新标准库类型来定义函数类型。function也是一个模板,function类型重载了调用运算符,该运算符接受它自己的实参然后将其传递给存好的可调用对象。

  1. int add(int a, int b)
  2. {
  3. return a + b;
  4. }
  5.  
  6. auto mod = [](int a, int b) {return a % b; };
  7.  
  8. struct divide
  9. {
  10. int operator() (int a, int b) { return a / b; }
  11. };
  12.  
  13. int main()
  14. {
  15. std::map<std::string, int(*)(int, int)> mapFunc =
  16. {
  17. { "+", add }, // 正确,add是一个函数指针
  18. { "/", divide() }, // 错误,不是一个函数指针
  19. { "*", mod} // 错误,lambda是个类类型
  20. };
  21.  
  22. std::function<int(int, int)> f1 = add; // 函数指针
  23. std::function<int(int, int)> f2 = divide(); // 函数对象类的对象
  24. std::function<int(int, int)> f3 = mod; // lambda
  25.  
  26. int ret;
  27. ret = f3(, ); // ret=1
  28.  
  29. std::map<std::string, std::function<int(int, int)>> mapFuncs =
  30. {
  31. {"+", add}, // 函数指针
  32. {"-", std::minus<int>()}, // 标准库函数对象
  33. {"/", divide()}, // 影狐定义的函数对象
  34. {"*", [](int a, int b) { return a * b; }}, // 未命名的lambda
  35. {"%",mod} // 命名了的lambda对象
  36. };
  37.  
  38. ret = mapFuncs["+"](, ); // ret = 3
  39.  
  40. return ;
  41. }

14.我们不能(直接)将重载函数的名字存入function类型的对象中,但是可以通过存储函数指针的方法。

  1. int add(int a, int b)
  2. {
  3. return a + b;
  4. }
  5.  
  6. double add(double a, double b)
  7. {
  8. return a + b;
  9. }
  10.  
  11. std::map<std::string, std::function<int(int, int)>> mapFunc =
  12. {
  13. { "+", add }, // 错误,分不清哪个add
  14. };
  15.  
  16. int(*fp)(int, int) = add;
  17. std::map<std::string, std::function<int(int, int)>> mapFuncs =
  18. {
  19. { "+", fp }, // 正确,是int型的add
  20. };

15.类型转换运算符是类的一种特殊成员函数,它负责将一个类类型的值转换成其他类型,一般形式如下:

operator type() const;

  • 其中type表示某种类型,类型转换运算符可以面向任意类型(void除外)进行定义,只要该类型能作为函数的返回类型。因此我们不允许转换成数组或函数类型,但允许转换成指针或引用类型。
  • 类型转换运算符既没有显示的返回类型,也没有形参,而且必须定义成类的成员函数。通常不应该改变转换对象的内容,因此一般被定义成const成员。
  • 因为类型转换运算符是隐式执行的,所以无法给这些函数传递实参,尽管类型转换函数不负责指定返回类型,但实际上每个类型转换函数都i会返回一个对应类型的值。
  • 尽管编译器一次只能执行一个用户定义的类型转换,但是隐式的用户定义类型转换可以置于一个标准类型转换之后或之前。
  1. class test
  2. {
  3. public:
  4. test(std::size_t size) : m_size(size) {}
  5. operator std::size_t() const { return m_size; }
  6.  
  7. private:
  8. std::size_t m_size;
  9. };
  10.  
  11. test t = ; // 首先将3隐式的转换成test,然后调用赋值运算符
  12. std::size_t i = t + ; // 首先将t隐式的转换为size_t,然后执行加法
  13. double d = t + 3.14; // t先被转换成了size_t,后又被转换成double
  14. test t1 = 3.14; // double被转换成了size_t

16.为了防止不必要的隐式自动类型转换,C++11引入了显示的类型转换运算符。

  1. class test
  2. {
  3. public:
  4. test(std::size_t size) : m_size(size) {}
  5. explicit operator std::size_t() const { return m_size; }
  6.  
  7. private:
  8. std::size_t m_size;
  9. };
  10.  
  11. test t();
  12. std::size_t s = t + ; // 错误,运算符是显示的
  13. std::size_t s1 = static_cast<std::size_t>(t) + ; // 正确

该规定存在一个例外,即如果表达式被用作条件,则编译器会将显示的类型转换自动应用于它:

  • if , while及do语句的条件部分
  • for语句头的条件表达式
  • 逻辑与或非运算符的运算对象
  • 条件运算符(? :)的条件表达式

17.如果类中包含一个或多个类型转换,则必须确保在类类型和目标类型之间只存在唯一一种转换方式。否则会产生二义性,只能通过显示调用转换运算符或转换构造函数来解决,强制类型转换也无法解决二义性问题。

  1. class testex;
  2. class test
  3. {
  4. public:
  5. test() {}
  6. test(const testex& t);
    };
  7.  
  8. class testex
  9. {
  10. public:
  11. operator test() const;
  12. };
  13.  
  14. test func(const test&);
  15. testex tex;
  16. test t1 = func(tex); // 二义性错误,我们想把tex转成test类型,但是有两种方法
  17. test t2 = func(tex.operator test()); // 正确
  18. test t3 = func(test(tex)); // 正确

18.当我们使用两个用户定义的类型转换时,如果转换函数之前或者之后存在标准类型转换,则标准类型转换将决定最佳匹配到底是哪个。

  1. class test
  2. {
  3. public:
  4. test(int i = ) {}
  5. test(double d) {}
  6.  
  7. operator int() const {}
  8. operator double() const {}
  9. };
  10.  
  11. void func(long double);
  12. test t;
  13. func(t); // 二义性错误,不知道调用哪个operator
  14.  
  15. long l;
  16. test t1(l); // 理论上也会产生二义性,此时标准类型转换将决定最佳匹配是哪个

19.如果我们对同一个类既提供了转换目标是算术类型的类型转换,也提供了重载的运算符,则将会遇到重载运算符与内置运算符的二义性问题。

  1. class test
  2. {
  3. friend test operator+(const test& t1, const test& t2);
  4. public:
  5. test(int i = ):m_count(i) {}
  6. operator int() const { return m_count; }
  7.  
  8. int m_count;
  9. };
  10.  
  11. test operator+(const test& t1, const test& t2)
  12. {
  13. return test(t1.m_count + t2.m_count);
  14. }
  15.  
  16. test t1, t2;
  17. test t3 = t1 + t2; // 正确
  18. int i = t3 + ; // 二义性错误

C++ Primer 笔记——重载运算的更多相关文章

  1. C++ Primer笔记

    C++ Primer笔记 ch2 变量和基本类型 声明 extern int i; extern int i = 3.14;//定义 左值引用(绑定零一变量初始值,别名) 不能定义引用的引用:引用必须 ...

  2. C++ Primer : 第十四章 : 重载运算与类型转换之重载运算符

    重载前须知 重载运算符是特殊的函数,它们的名字由operator和其后要重载的运算符号共同组成. 因为重载运算符时函数, 因此它包含返回值.参数列表和函数体. 对于重载运算符是成员函数时, 它的第一个 ...

  3. 高放的c++学习笔记之重载运算与类型转换

    ▲基本概念 (1)重载运算符是具有特殊名字的函数,它们的名字又operator和其后要定义的运算符号共同构成.. (2)对于一个运算符号来说它或者是类的成员,或者至少含有一个类类型的参数. (3)我们 ...

  4. C++ Primer笔记10_运算符重载_赋值运算符_进入/输出操作符

    1.颂值运营商 首先来福值运算符引入后面要说的运算符重载.上一节说了构造函数.拷贝构造函数:一个类要想进行更好的控制.须要定义自己的构造函数.拷贝构造函数.析构函数.当然,还有赋值运算符.常说的三大函 ...

  5. C++ Primer 5th 第14章 重载运算与类型转换

    当运算符作用域类类型的对象时,可以通过运算符重载来重新定义该运算符的含义.重载运算符的意义在于我们和用户能够更简洁的书写和更方便的使用代码. 基本概念 重载的运算符是具有特殊名字的函数:函数名由关键词 ...

  6. 【c++ Prime 学习笔记】第14章 重载运算与类型转换

    14.1 基本概念 重载的运算符是特殊的函数:名字由关键字operator后接要定义的算符共同组成,也有返回类型.参数列表.函数体. 重载运算符函数的参数量与该算符作用的运算对象数量一样多 除重载调用 ...

  7. C++ Primer笔记13_运算符重载_总结

    总结: 1.不能重载的运算符: . 和 .* 和 ?: 和 ::  和 sizeof 和 typeid 2.重载运算符有两种基本选择: 类的成员函数或者友元函数, 建议规则例如以下: 运算符 建议使用 ...

  8. C++ Primer笔记12_运算符重载_递增递减运算符_成员訪问运算符

    1.递增递减运算符 C++语言并不要求递增递减运算符必须是类的成员.可是由于他们改变的正好是所操作对象的状态.所以建议设定为成员函数. 对于递增与递减运算符来说,有前置与后置两个版本号,因此.我们应该 ...

  9. C++primer笔记之顺序容器

    最近又重新拾起C++primer,发现每一次看都会有不同的体验,但每一次看后因为不常用,忘记得很快,所以记笔记是很关键的一环,咋一看是浪费时间,实际上是节省了很多时间.下面就把这一节的内容做一个简单的 ...

随机推荐

  1. X86架构

    在接触BIOS的时候,都需要对PC架构有一定的认知.目前的PC架构绝大多数都是Intel的X86架构,貌似也是因为INTEL的这个X86架构早就了目前INTEL如日中天的地位. 废话不多说,X86架构 ...

  2. 浅谈Linux下CPU利用率和CPU负载【转】

    转自:https://blog.csdn.net/Alisa_xf/article/details/71430406 在Linux/Unix下,CPU利用率(CPU utilization)分为用户态 ...

  3. requests库入门04-http基本认证

    因为后续样例中GitHub都需要提供认证,所以先写关于基本认证的 http的请求中,有一些请求是需要通过授权认证之后才会响应,授权认证就是检查用户名和密码的过程.http有一个基本认证方式,在认证的过 ...

  4. mongodb管理与安全认证

    mongodb数据管理 数据的导出.数据导入数据导出 mongoexport [使用mongoexport -h查看参数] 数据导入 mongoimport [使用mongoimport -h查看参数 ...

  5. 【转】thread.sleep(0)与thread.sleep(1)的区别

    Thread.Sleep(0) Sleep的意思是告诉操作系统自己要休息n毫秒,这段时间就让给一个就绪的线程吧.当n=0时,意思是要放弃自己剩下的时间片,但是仍然是就绪状态.Sleep(0)只允许那些 ...

  6. SIFT+BOW 实现图像检索

    原文地址:https://blog.csdn.net/silence2015/article/details/77374910 本文概述 图像检索是图像研究领域中一个重要的话题,广泛应用于医学,电子商 ...

  7. 基于OpenSSL自建CA和颁发SSL证书

    关于SSL/TLS介绍见文章 SSL/TLS原理详解.关于证书授权中心CA以及数字证书等概念,请移步 OpenSSL 与 SSL 数字证书概念贴 . openssl是一个开源程序的套件.这个套件有三个 ...

  8. Fiddler功能介绍

    1.对话框:添加备注,添加完了会在控制面板中的comments显示2.Replay:选中会话后点击,会重新发送请求3.Go:是打断点后,想要继续执行,就点击GO 4.Stream:模式切换. 默认是缓 ...

  9. pwnable.kr simple login writeup

    这道题是pwnable.kr Rookiss部分的simple login,需要我们去覆盖程序的ebp,eip,esp去改变程序的执行流程   主要逻辑是输入一个字符串,base64解码后看是否与题目 ...

  10. 转载:UML学习(三)-----序列图(silent)

    原文:http://www.cnblogs.com/silent2012/archive/2011/09/14/2172219.html UML的模型中可分为两种,动态模型和静态模型.用例图.类图和对 ...