zoukankan      html  css  js  c++  java
  • Chapter14:重载运算符

    对于一个运算符函数来说,它或者是类的成员,或者至少含有一个类类型的参数。

    1 int operator+(int, int);//错误,不能为int重定义内置运算符

    对于一个重载的运算符来说,其优先级和结合律与对应的内置运算符保持一致。

    1 x==y+z
    2 //永远等价于
    3 x == (y+z)

    逻辑与运算、逻辑或运算和逗号运算符的运算对象求值顺序规则无法保留下来,所以不建议重载;

    逗号运算发和取地址运算符不重载的另一个原因:C++语言已经定义了这两个运算符用于类类型对象时的特殊含义。

    尽量明智地使用运算符重载(慎用)

    每个运算符在用于内置类型时都有比较明确地含义。当在内置的运算符和我们自己的操作之间存在逻辑映射关系时,运算符重载的效果最好。此时,使用重载的运算符显然比另一个名字更自然也更直观。不过,过分滥用运算符也会使我们的类变得难以理解。

    只有当操作的含义对于用户来说清晰明了时才使用运算符。

    成员函数&非成员函数

    赋值(=),下标([]),调用(())和成员访问箭头(->)运算符必须是成员;

    复合赋值一般来说应该是成员,但并非必须,这一点与赋值运算符略有不同。

    改变对象状态的运算符或者给定类型密切相关的运算符,如递增、递减、和解引用运算符,通常应该是成员;

    具有对称性的运算符通常应该是普通的非成员函数。

    关于<运算符

    Best Practice:如果存在唯一一种逻辑可靠的<定义,则应该考虑为这个类定义<运算符。如果类同时还包含==,则当且仅当<的定义和==产生的结果一致时才定义<运算符。

    关于下标运算符

    我们最好同时定义下标运算符的常量版本和非常量版本,当作用于一个常量对象时,下标运算符返回常量引用以确保我们不会给返回的对象赋值。

    1 string& operator[] (size_t n);
    2 const string& operator[] (size_t n) const;

    关于箭头运算符

    point->mem:point必须是指针类型或是重载了operator->的类对象。

    根据point的不同,point->mem等价于:

    1.(*point).mem;//point是指针类型

    2.point.operator()->mem;//point是类的一个对象。

    那么这一表达式是如何执行的呢?

    如果point是指针,则执行(1);

    如果point是类对象,则使用point.operator->()的结果来获取mem;其中,如果该结果是一个指针,则执行第1步;如果该结果本身是重载了operator->()的对象,则重复执行(2)

    lambda与函数对象

    默认情况下,lambda产生的类不能改变它捕获的变量,所以函数调用操作符是一个const成员函数。

    lambda表达式产生的类不含默认构造函数、赋值运算符及默认析构函数,它是否含有默认的拷贝/移动构造函数则通常视捕获的数据成员类型而定。

    可调用对象与function

    调用形式(call signature):调用返回类型+参数类型就是一种调用形式。所以,两种不同的可调用对象可能共享同一种调用形式。

    1 int add(int i, int j) { return i + j; }
    2 auto mod = [](int i, int j) {return i%j; };
    3 struct divide
    4 {
    5     int operator()(int i, int j) { return i / j; }
    6 };

    假设我们要将这些可调用对象统一到函数表中

    1 map<string, int(*)(int, int)> binops;
    2 binops.insert({ "+",add });
    3 binops.insert({ "%",mod });//错误,mod不是函数指针

    如何解决这一问题?

    function标准库

    1 map<string, function<int(int, int)>> binops;
    2 binops.insert({ "+",add });
    3 binops.insert({ "%",mod });

    但是一个问题是:重载函数

    1 int add(int i, int j) { return i + j; }
    2 Sale_data add(const Sale_data&, const Sale_data&);
    3 map<string, function<int(int, int)>> binops;
    4 binops.insert({ "+",add });//哪个add?

    两种解决方法:

    1 binops.insert({ "+",fp });
    2 //或者
    3 binops.insert({ "+",[](int a,int b) {return add(a,b); } });

    类型转换

    之前我们通过转换构造函数来实现了用户定义的类型转换。

    现在我们提供另一种方法:定义类型转换运算符。

    operator type() const;

    一个类型转换函数必须是类的成员函数;它不能声明返回类型,形参列表也必须为空。通常应该是const。

    之前我们提到过:编译器一次只能执行一次用户定义的类型转换,但是隐式的用户类型转换可以置于一个标准(内置)类型转换之前或之后。

    避免过度使用类型转换函数:除非两种类型之间存在明确地一对一映射

    在实践中,类很少提供类型转换运算符。在大多数情况下,如果类型转换自动发生,用户可能会感觉比较意外,而不是感觉受到了帮助。然而这条经验法则存在一种例外情况:对于类来说,定义向bool的类型转换还是比较普遍的现象

    一个问题:

    1 int i = 42;
    2 cin << i;

    因为cin没有>>,所以,cin会转换成bool类型,然后左移。

    为了防止这一情况的发生,C++新标准引入了显式地类型转换运算符。

    该规定存在一个例外:如果表达式被用作条件,则编译器会将显式地类型转换自动应用于它。由此,解决这一问题

    二义性类型转换(要避免)

    二义性1:

     1 struct B;
     2 struct A
     3 {
     4     A() = default;
     5     A(const B&);
     6 };
     7 struct B
     8 {
     9     operator A() const;
    10 };
    11 A f(const A&);
    12 B b;
    13 A a = f(b);//究竟是f(B::oprator A())还是f(A::A(const B&))

    二义性2:

     1 struct A
     2 {
     3     A(int = 0);
     4     A(double);
     5     operator int() const;
     6     operator double() const;
     7 };
     8 void f2(long double);
     9 A a;
    10 f2(a);//转换为int还是double?
    11 long lg;
    12 A a2(lg);//A::A(int)还是A::A(double)

    由此:

    不要令两个类执行相同的类型转换;(只有A转换为B,或者B转换为A)

    避免转换目标是内置算术类型的转换。特别是当你已经定义了一个转换为算术类型的转换时,接下来:

    不要再定义接受算术类型的重载运算符;

    不要再定义转换到多种类型的类型转换。

    一言以蔽之:除了显式地向bool类型转换,我们应该尽量避免定义类型转换函数并尽可能限制那些“显然正确”的飞显式构造函数

    重载函数与重载运算符的一个不同点是

    具有相同名字的成员函数和非成员函数不会彼此重载。

    而a sym b可能是 a.operatorsym(b)或者operatorsym(a,b)

    所以,函数匹配过程中,成员函数和非成员函数都应该考虑在内。

  • 相关阅读:
    [转]MongoDB for Java】Java操作MongoDB
    [转]Apache Maven 入门篇 ( 上 )
    [转]Apache Maven 入门篇(下)
    [转]-Dmaven.multiModuleProjectDirectory system propery is not set. 解决方案 适用于myeclipes 和 eclipes
    [转]java代码注释规范
    [转] java编程规范
    [原创]java WEB学习笔记43:jstl 介绍,core库详解:表达式操作,流程控制,迭代操作,url操作
    [转]详解Java解析XML的四种方法
    [原创]java WEB学习笔记42:带标签体的自定义标签,带父标签的自定义标签,el中自定义函数,自定义标签的小结
    [原创]java WEB学习笔记41:简单标签之带属性的自定义标签(输出指定文件,计算并输出两个数的最大值 demo)
  • 原文地址:https://www.cnblogs.com/wangyanphp/p/5846580.html
Copyright © 2011-2022 走看看