首页 > 编程语言 >C++:重载运算符

C++:重载运算符

时间:2022-12-24 21:34:31浏览次数:60  
标签:const 函数 C++ 运算符 operator 重载 Foo

基本概念

通常我们自定义的类类型,不具有内置类型的一些操作,比如 int 类型的算术运算,指针类型的解引用、取地址操作,容器类型的下标操作等。因此,如果希望我们自定义的类类型具有一些运算符操作,就需要定义重载运算符函数,实现对应的功能。

重载运算符是具有特殊名字的函数:它们的名字由关键字 operator 和其后要定义的运算符号共同组成。

[返回类型] operator[运算符]([参数]) {}

和其他函数一样,重载的运算符也包含返回类型、参数列表以及函数体。

重载运算符的特点

  1. 重载运算符函数的参数数量与该运算符作用的运算对象数量一样多。一元运算符有一个参数,二元运算符有两个参数。
  2. 对于二元运算符来说,左侧运算对象传递给第一个参数,右侧运算对象传递给第二个参数。
  3. 除了重载调用运算符 operator() 的函数外,其他重载运算符不能含有默认实参。
  4. 如果一个运算符函数是成员函数,则它的第一个(左侧)运算对象绑定到隐式的 this 指针上。因此,成员运算符函数的(显式)参数数量比运算符的运算对象总数少一个。
  5. 对于一个运算符函数来说,它或是类的成员,或者至少有一个类类型的参数。也就是说内置类型不能有重载运算符函数。
  6. 不是所有的运算符都可以被重载,逻辑与、逻辑或、逗号运算符和取地址运算符不能被重载。
  7. 赋值(=)、下标([])、调用(())和成员访问箭头(->)运算符必须是成员函数。
  8. 改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减和解引用运算符,通常应该是成员函数
  9. 具有对称性的运算符可能转换任意一端的运算对象,如算术、相等性、关系和位运算符等,通常应该是普通函数

输入和输出运算符

输出运算符 << 的第一个形参是一个非 constostream 对象的引用。非 const 是因为需要修改 ostream 对象的状态,引用是因为无法对 ostream 进行拷贝。第二个形参是想打印的类类型的 const 引用。

std::ostream &operator<<(std::ostream &os, const Foo &foo) {
    os << foo.data1 << foo.data2;
    return os;
}

输入运算符 >> 的第一个形参是运算符要读取的流的引用,第二个形参是要读到的对象的引用,区别于输出运算符使用 const 引用的原因是要修改对象的内容。

std::istream &operator>>(std::istream &is, Foo &foo) {
    is >> foo.data1 >> foo.data2;
    if (is) {  // 输入运算符需要判断读取流是否成功。
        //
    } else {
        foo = Foo();
    }
    return is;
}

输入和输出运算符必须是非成员函数,输入运算符重载函数需要处理读错误的情况。

算数和关系运算符

通常情况下,需要把算术和关系运算符定义为非成员函数,是因为允许左侧和右侧运算对象转换。算数运算符通常会计算两个运算对象并得到一个新对象,并且新对象是函数内部栈区的,所以通常返回对象的副本,而非引用。算数和关系运算符也不需要更改对象状态,所以输入的两个运算对象通常是 const 的引用。

Foo operator+(const Foo &lhs, const Foo &rhs) {
    Foo tmp = lhs;
    tmp += rhs;
    return tmp;
}

一般情况下,如果重载了算数运算符,通常会一起重载复合运算符,并且算数运算符会使用复合运算符实现。

Foo operator+=(const Foo &lhs, const Foo &rhs) {
    Foo tmp = lhs;
    tmp.data1 += rhs.data1;
    tmp.data2 += rhs.data2;
    return tmp;
}

同样,关系运算符一般情况下也是把相关的运算符一并实现。

bool operator==(const Foo &lhs, const Foo &rhs) {
    return lhs.data1 == rhs.data1 && lhs.data2 == rhs.data2;
}

bool operator!=(const Foo &lhs, const Foo &rhs) {
    return !(lhs == rhs);
}

赋值运算符

除了在 C++:构造函数与拷贝控制 中介绍的两个赋值运算符之外,C++11 还支持使用花括号的方式对对象进行赋值操作。

Foo foo = {1, 2, 3};

这归功于重载了使用初始化列表参数的赋值运算符,不伦那种类型的赋值运算符都必须是成员函数。

Foo &operator(initializer_list<T> il) {
    // 
    return *this;
}

下标运算符

下标 [] 操作是容器类用来通过位置访问容器中的元素的成员函数,所以重载下标操作符函数必须是成员函数。通常情况下,需要重载常量版本(返回 const 引用)和非常量版本(返回引用)的下标运算符,常量版本返回的元素不可以被修改。

Foo &operator[](int n) {
    return this.data[n];
}

const Foo &operator[](int n) const {
    return this.data[n];
}

当使用常量对象运行下标操作时,调用的是常量版本的下标操作符函数。

Foo f1;
f1[1] = "aa";  // 正确,调用非常量版本下标操作符函数,返回值可以修改。
 
const Foo f2;
f2[1] = "bb";  // 错误,调用常量版本下标操作符函数,返回值不可以修改。

递增和递减运算符

递增、递减操作符分为前置递增递减和后置递增递减,为了区分前置和后置,后置操作符的形参是一个值为 0 的实参,通常不写。递增、递减运算符通常为成员函数,其中后置版本由于需要记录中间值,所以返回的是局部对象的副本而非引用。

Foo &operator++() {
    // 前置递增
    check(cur); // 需要检测有效性
    ++cur; 
    return *this;
}

Foo &operator--() {
    // 前置递减
    check(cur); // 需要检测有效性
    --cur;
    return *this;
}

Foo operator++(int) {
    // 后置递增
    Foo foo = *this;
    ++*this;   // 调用前置递增
    return foo;
}

Foo operator--(int) {
    // 后置递减
    Foo foo = *this;
    --*this;   // 调用前置递减
    return foo;
}

成员访问运算符

箭头(->)运算符必须是成员函数,解引用运算符(*)通常是成员函数。箭头、解引用通常是常函数,但是返回值不是常量的。

[返回类型] &operator*() const {}
[返回类型] *operator->() const {}

函数调用运算符

如果类重载了函数调用运算符,则可以像使用函数一样的使用类,该类也称为函数对象。函数调用运算符必须是成员函数,根据对象内容返回相应的类型,所以函数调用运算符函数,可以有多个重载。因为不能改变内部状态,所以是 const 的成员函数。

[返回类型] operator() const {}

类型转换运算符

类型转换运算符是类的特殊成员函数,负责将一个类类型的值转换为其他类型。类型转换运算符没有显式的返回值,没有形参,但是必须是成员函数,应该是 const 的。

operator [type]() const {}

标签:const,函数,C++,运算符,operator,重载,Foo
From: https://www.cnblogs.com/hiyoung/p/17003416.html

相关文章

  • C++科研人员信息管理系统
    C++科研人员信息管理系统某科研团队主要有四类人员:科研主管、研究员、研究助理和实习研究员。现在,需要存储这些人员的姓名、编号、级别、当月薪水,计算月薪总额并显示全部......
  • C/C++在线餐馆预订管理系统
    C/C++在线餐馆预订管理系统软件学院实训任务书一、实训名称实践环节数据结构与算法实训项目名称在线餐馆预订管理系统二、学生信息班级......
  • c++ 模板参数有默认值时模板特例化匹配问题
    如下的源码:template<typenameT,typenameU=int>classS{//#1public:voidf1(){};};template<>classS<void>{ //#2public:voidf2(){};};......
  • 蓝桥-13届-C++-B组-省赛-F题-统计子矩阵
    直达链接主要解题思路分为两个部分,1是构造二维前缀和计算矩阵和,降低每次求和的时间复杂度;2是对所有子矩阵的遍历求和过程,因为需要两个坐标,遍历4个行/列值,4层for循环时间复......
  • C++基础3
    C++基础3typedef为现有类型创建一个新名字主要有以下几种形式:为基本数据类型定义别名为指针定义别名为自定义数据类型定义别名为数组定义别名声明函数定义新......
  • C/C++编译器配置——MinGW下载安装
    C/C++编译器配置——MinGW下载安装前言本文主要讲述如何安装C语言编译器——MinGW,特点是文章附有完整详细的实际安装过程截图,文字反而起说明提示作用。编写本文的原因......
  • 国产paozhu c++ web framework 正式版发布
    经过大半个月测试修改paozhuc++webframework正式版发布,1.0.5release官方第一次发布正式版,可以用于生产环境。易用性超越国外各种c++webframework,简单易用,新手......
  • C++11:返回值类型后置(跟踪返回值类型)
    返回值类型后置语法,是为了解决函数返回值类型依赖于参数而导致难以确定返回值类型的问题。有了这种语法以后,对返回值类型的推导就可以用清晰的方式(直接通过参数做运算)描述......
  • C++进阶(哈希)
    vector容器补充(下面会用到)我们都知道vector容器不同于数组,能够进行动态扩容,其底层原理:所谓动态扩容,并不是在原空间之后接续新空间,因为无法保证原空间之后尚有可配置的空间......
  • C++——贪心
     5745:演讲大厅安排描述有一个演讲大厅需要我们管理,演讲者们事先定好了需要演讲的起始时间和中止时间。我们想让演讲大厅得到最大可能的使用。我们要接受一些预定而拒......