IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++运算符重载相关的几个问题 -> 正文阅读

[C++知识库]C++运算符重载相关的几个问题

1. 为什么需要重载运算符:

C++预定义的运算符,只能用于基本数据类型的运算:整型、实型、字符型、逻辑型等,且不能用于对象的运算。
当我们需要在对象之间进行运算时,就需要重载运算符。

如果不重载运算符,实际上也可以完成逻辑运算,但是每次需要对函数对象进行运算操作时都要手动实现一次比较麻烦, 重载运算符本质上相等于是对类对象的运算封装成了一个operator函数,每次需要运算时直接调用operator函数即可,更简便、直观。

2. C++运算符重载的基本形式:

返回值类型 + operator关键字 + 待重载的运算符(函数形参列表) 
{
    函数体
}

3. C++运算符重载举例:

#include <iostream>
using namespace std;

//Complex类表示一个复数
class Complex {
public:
    Complex() = default;
    Complex(double r = 0.0, double i = 0.0) : m_real(r), m_imag(i) {}

    Complex& operator=(const Complex& rhs) { //赋值运算符
        this->m_real = rhs.m_real;
        this->m_imag = rhs.m_imag;
        return *this;
    }

    Complex operator+(const Complex& rhs) {
        //返回的是一个临时对象,所以返回值类型不是引用
        return Complex(this->m_real + rhs.m_real, this->m_imag + rhs.m_imag);
    }

    friend Complex operator-(const Complex& lhs, const Complex& rhs);

    bool operator==(const Complex& rhs) {
        if(this->m_real == rhs.m_real && this->m_imag == rhs.m_imag) {
            return true;
        }
        return false;
    }

    void print() {
        cout << m_real << '.' << m_imag << endl;
    }

private:
    double m_real;
    double m_imag;
};


//非成员函数,类的友元函数
Complex operator-(const Complex& lhs, const Complex& rhs) {
    return Complex(lhs.m_real - rhs.m_real, lhs.m_real - rhs.m_real);
}

int main() {
    Complex a(2, 2);
    Complex b(1, 1);

    Complex c = a + b;	//等价于 a.operator+(b);
    Complex d = a - b;	//等价于 operator-(a, b);

    c.print();
    d.print();

    return 0;
}

------

3.3
1.1

本示例代码中的注意事项:

  1. 重载运算符作为类的成员函数与普通函数时的区别:

在上面的例子中,operator+ 是Complex类的成员函数,operator- 是Complex类的非成员函数(friend友元函数),从中可以看出,重载运算符在作为类的成员函数 与 普通函数 时的区别:

重载运算符为成员函数时,函数参数的个数为运算符的目数减一。
这是因为类的成员函数有一个隐式参数*this指针。

  1. operator+ 函数的入参与返回值:

operator+ 和 operator- 的入参是 const Complex& 引用类型,这是因为如果使用 “值传递” 的参数类型,会在入参时构造一个Complex对象,调用类的构造函数、析构函数,这会增加函数开销,所以正确的做法是使用引用类型传参;

operator+ 和 operator- 的返回值类型是Complex对象而不是引用,因为它们需要返回一个新对象给左值。

4. C++中哪些运算符不能被重载:

C++中的运算符可分为 9大类:

1. 赋值运算符:	=
2. 算符运算符:	+, -, *, /, %
3. 逻辑运算符:	&&, ||, !
4. 关系运算符:	>, <, >=, <=, ==, !=
5. 位操作符:		~(取反), <<(左移), >>(右移), &(位与), |(位或), ^(位异或)
6. 自增自减运算符:	++, --
7. 复合运算符:	+=, -=, *=, /=, %=, <<=, >>=
8. 条件运算符: ? :
9. 逗号运算符:	,

------
 
 这其中,有 1类运算符不能被重载:
 条件运算符:	? :

常见的一些操作符:

1. 取地址操作符:	&
2. 解引用操作符:	*
3. 调用操作符:		()	(调用操作符是一对圆括号,括住传递函数的实参列表)
4. 箭头操作符:	->	
5. 作用域操作符:	::
6. 点操作符:			.
7. 下标操作符:		[]
8. new/delete([])操作符:	[]
9. 成员指针访问操作符:	->*, .* (当要访问的类的成员是指针类型时使用)
10.输入和输出操作符:	>>, <<

------

这其中,有 3类操作符不能被重载:
作用域操作符:		::
点操作符:				.
成员指针访问操作符:	.*

总结:
C++中绝大部分的操作符允许重载,不能重载的运算符只有5个:

.				:	成员访问运算符
.*				:	成员指针访问运算符
::				:	域运算符
? :			:	条件运算符
sizeof		:	长度运算符

5. C++ 流插入和流提取运算符的重载:

std::cout << "hello";

这条语句是如何起作用的?

  • 实际上,cout 是在 iostream 头文件中定义的 ostream类的对象;
  • <<运算符 能够用在 cout 上是因为,在 ostream类对<<运算符进行了重载。

实际上的效果是这样的:

// #include <iostream> //iostream 头文件中:

class ostream {
public:
	ostream& operator<<(const int n) {

    }
    
    ostream& operator<<(const char* s) {

    }
};

ostream cout;		//cout是ostream类的对象,cout也是ostream类型

cout<< 1 ;
等价于:
cout.operator<<(1);

因此,如果想要把我们自定义的类型通过 cout << 的方式进行打印,就需要重载 ostream类的流插入运算符<<

#include <iostream>
using namespace std;

class Complex {
public:
    Complex(double real = 0.0, double imag = 0.0) : m_real(real), m_imag(imag) {}

    friend ostream& operator<<(ostream& o, const Complex& s);

private:
    double m_real;
    double m_imag;
};

ostream& operator<<(ostream& o, const Complex& s) {
    o << s.m_real << '.' << s.m_imag;
    return o;
}

int main() {
    Complex c(1, 2);
    cout << c << endl;

    return 0;
}

------
运行结果:
1.2

------
解释:
"cout << c" 这句相当于是调用了代码中的 operator<< 运算符重载函数,
其中: cout 是operator<<(ostream& o, const Complex& s) 中的第一个参数入参,Complex类对象c是函数的第二个参数入参。
返回值 ostream& 只是一种书写习惯,没有实际作用。

《C++ Primer》中对于 重载运算符<< 的表述:

??通常情况下,输出运算符的第一个形参是一个非常量ostream对象的引用。之所以ostream是非常量是因为向流写入内容会改变其状态(实际上是向cout对象中写入内容);而该形参是引用是因为我们无法直接复制一个ostream对象。

??第二个形参一般来说是一个常量的引用,该常量是我们要打赢的类类型。第二个形参是引用的原因是我们希望避免复制实参;而之所以该形参可以是常量是因为(通常情况下)打印对象不会改变对象的内容。

??输入输出运算符必须是非成员函数,且必须是friend友元函数。

重载流提取运算符>> 举例:

#include <iostream>
using namespace std;

class Complex {
public:
    Complex() = default;

    friend istream& operator>>(istream& i, Complex& c);
    friend ostream& operator<<(ostream& o, const Complex& c);
private:
    double m_real;
    double m_imag;
};

istream& operator>>(istream& i, Complex& c) {
    i >> c.m_real >> c.m_imag;
    return i;
}

ostream& operator<<(ostream& o, const Complex& c) {
    o << c.m_real << '.' << c.m_imag;
    return o;
}

int main() {
    Complex c;
    cin >> c;
    cout << c << endl;

    return 0;
}

------
运行结果:
1
2
1.2

6. C++自增运算符和自减运算符的重载:

  • 自增运算符和自减运算符的特殊之处在于有前置和后置之分,而普通的重载形式无法区分这两种情况。为了解决这个问题,后置版本接受一个额外的(不被使用)int类型的形参。当我们使用后置运算符时,编译器为这个形参提供一个值为0的实参。这个形参的唯一作用就是区分前置版本和后置版本的函数,而不是真的要在实现后置版本时参与运算。
  • C++语言并不要求递增和递减运算符必须是类的成员,但是因为它们改变的正好是所操作对象的状态,所以 建议将其设定为成员函数
  • 为了与内置版本保持一致:前置运算符应该返回递增或递减后 对象的引用;后置运算符应该返回对象的原值(递增或递减之前的值),返回的形式是一个值而非引用
  • 对于后置版本,在递增/递减对象之前,需要先记录对象的状态(额外定义一个临时变量,这也是后置版本的性能不及前置版本的原因), 然后递增/递减对象的值,但是返回原值。

举例:

class StrBlobPtr() {
public:
    StrBlobPtr& operator++();		//前置运算符
    StrBlobPtr& operator--();
    
    StrBlobPtr operator++(int);		//后置运算符
    StrBlobPtr operator--(int);
private:
    int curr;
};

StrBlobPtr& operator++() {
    ++curr;
    return *this;
}
StrBlobPtr& operator--() {
    --curr;
    return *this;
}

StrBlobPtr operator++(int) {
    StrBlobPtr ret = *this;		//对于后置版本,在递增对象之前需要记录对象的状态,这也是后置版本的性能不如前置版本的原因
    ++*this;
    return ret;		//递增/递减对象的值,但是返回原值
}
StrBlobPtr operator--(int) {
    StrBlobPtr ret = *this;
    --*this;
    return ret;
}

显式的调用后置运算符:

StrBlobPtr p(a1);
p.operator++(0);		//后置版本
p.operator++();			//前置版本
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-02 11:09:57  更:2021-09-02 11:10:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 20:22:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计