c++笔记——运算符重载

运算符重载在c++中是经常用到的一个知识点,也是c++富有扩展性的重要支持点。

今天我准备把运算符重载在各种情况下的应用,都一一举例,待会我们就会知道运算符重载的威力和带来的便捷性。

运算符重载函数的两种形式:

1、作为类的成员函数

赋值(=)、下标([])、调用( () )、成员访问箭头(->) , 这四个运算符必须是成员函数,其他运算符既可以是成员函数,也可以使非成员函数。

注意点:如果我们把运算符函数定义成成员函数时,它的左侧运算对象必须是所属类的一个对象。

2、作为非成员函数

一般情况下,只有具有对称性的运算符才会定义成非成员函数(比如: &&、==) , 非成员函数,一般情况下都是类的友员函数。

由此可以看出,如果一个运算符被定义为非成员函数的形参数量比其定义为成员函数多一个。

下面是各种运算符的重载

1、重载输入输出运算符

<span style="font-size:12px;">class base
{
public:
    base() {}
    base(int new_x , double new_y):
        x(new_x) , y(new_y)   {}
    friend istream &operator>>(istream &in , base &chs);
    friend ostream &operator<<(ostream &out , base &chs);
private:
    int x;
    double y;
};
istream &operator>>(istream &in , base &chs)
{
    in>>chs.x>>chs.y;
    return in;
}

ostream &operator<<(ostream &out , base &chs)
{
    out<<chs.x<<"  "<<chs.y;
    return out;
}</span>

如上图的代码:

1、输入输出必须得定义成友元函数,因为如果我们定义为成员函数,那么就意味着第一个参数必须是所在类的对象,则我们调用的时候就得这样:

<span style="font-size:14px;">    base x ;
    x>>cin;</span>

2、我们一般情况下都要返回流的引用,因为这是为了可以连续的输入: cin>>x>>y;

2、算术和关系运算符

算数运算符主要是普通的运算符,如: + - * / && || !等运算符

其中,算术运算符和关系运算符一般都是非成员函数。

下面解释为什么算术运算符和关系运算符不能是成员函数:

1、如果运算符 ‘+’ 是成员函数

class base
{
public:
    base() {}
    base(int new_x , double new_y):
        x(new_x) , y(new_y)   {}
    <span style="color:#ff0000;">base operator +(const base &chs)
    {
        x += chs.x;
        y += chs.y;
        return *this;
    }</span>
private:
    int x;
    double y;
};
base x(1 , 2.1) , y(3 , 3.4) , z;
z = x+y;                //这个时候即改变了x , 也改变了 z , 但我们的本意是只改变在 , 而不改变x;

2、下面是友元函数

base operator +(const base &ch , const base &chs)
{
    base bz = ch;
    bz.x += chs.x;
    bz.y += chs.y;

    return bz;
}
<pre class="cpp" name="code">base x(1 , 2.1) , y(3 , 3.4) , z;
z = x+y;   // 这个时候,我们由于在函数中用了一个中间变量,因此只改变了 z , 而x不变

通过上面我们知道,对于一个运算符重载的函数形式很关键,如果用错了函数形式,那么可能会带来意料不到的危险。

因此,当我们要写一个运算符重载的函数时,我们首先要弄清这个运算符是干什么了 ,在内置类型上有哪些用法,我们再根据这些用法去决定,是要写成成员函数 , 还是非成员函数。

3、下标运算符 [ i ]

下标运算符主要是用于数组、指针、容器等,下标运算符的作用有下面几点:

1、通过下标运算符,得到数组中该位置的值

2、我们还需要通过下标运算符改变该位置的值

我们能知道,下标运算符一定是要作为成员函数。

根绝上面的作用分析,我们可以得到

1、函数需要一个整形形参,告诉函数要去哪个位置的值

2、函数必须得返回该位置的引用值

3、我们需要重载一个常量版本的函数。

由上面,我们就得到了代码:

char &base::operator [](int i)
{
    if(i >= length)  return chs[0];
    return chs[i];
}

const char &base::operator [](int i) const
{
    if(i >= length)  return chs[0];
    return chs[i];
}

4、递增递减运算符

在迭代器或平时的内置类型上,我们经常都会使用 (--)和(++)运算符,对于初学者来说,他们肯定特想把这两个运算符弄死掉。下面我们就以(++)运算符,来分析其作用;

对于递增(++)运算符的作用

1、运算符在变量前面,直接递增变量的值

2、运算符在变量后面,这时我们就要先保存对象的状态,然后再把 递增的值传给他

对于这两个运算符,我们一般把函数写为成员函数。

我们先写递增运算符前置的函数,前置时,我们返回的是该变量自身的引用

base &base::operator ++()
{
    x += 1;
    return *this;
}

递增运算符(++) 后置,这时我们就要考虑怎么才能实现,先保存其状态?

我们这样,我们先返回一个中间变量 , 那我们下次调用该变量时,就是递增之后的变量了。

由于我们返回的是中间变量,所以我们不能返回引用了。

<span style="color:#000000;">base base::operator ++<span style="color:#cc0000;">(int)</span>
{
    base bz = *this;
    x += 1;
    return bz;
}</span><span style="color:#990000;">
</span>

之所以加上一个整形形参,是因为编译器需要区分前置和后置两个情况。因此下次调用时,我们就必须传递一个整形给它

5、函数调用运算符 ()

函数调用运算符必须得是成员函数。

函数调用运算符,使得我们可以像调用函数一样,调用该类的对象

class base
{
public:
    void operator ()()    //没有参数的函数调用运算符
    {
        cout<<x<<endl;
    }
    void operator() (int z)     //存在一个整形形参
    {
        cout<<x+z<<endl;
    }
private:

    int x;
};
//下面是调用形式
base x;
x();            //调用第一个函数
x(4);              //调用第二个函数

c++中的lambda表,就类似于在一个类中重载了函数调用运算符函数。

6、类型转换运算符 , int    double

类型转换运算符负责把类转换成另个需要的类型

因此,其没有显示的返回类型,也没有任何参数。

class base
{
public:
    operator int()
    {
        return x;              //把类类型转黄成一个整形
    }
private:

    int x;
};
//这是一个既可以隐身转换,也能显示转换的
base x;
int z = x , y = static_cast<int>(x);
//如果在函数前面加上explicit就只能显示的转换
explicit operator int();
int x = z;    //这是错误的 , 不能隐式转换
int x = static_cast<int>(x);

当我们重载类型转换运算符时,我们一定要小心二义性。

运算符重载,确实给我们带来了很大自由和便捷。但同时它也带来了危险,因此在使用的时候我们一定要对家注意。

我们重载运算符时,我们要记住,重载的运算符,一定要符合这个运算符本身的特性,也要尽量满足我们再内置类型上对这个运算符的使用习惯。

共勉。



c++笔记——运算符重载

时间: 2024-10-19 08:53:49

c++笔记——运算符重载的相关文章

0718-----C++Primer听课笔记----------运算符重载

0.两个要点 a) 一个空类,编译器自动合成默认无参构造函数.析构函数.拷贝构造函数.赋值运算符. b) 在编写类的时候,必须严格区分对象是否可复制. 1.运算符重载之 string类 1.1 运算符重载的几个要点: a) 运算符重载可以有成员函数和友元函数的形式,后者比前者多一个参数. b) =和+=必须重载为成员函数的形式(不太理解原因). c) 输入和输出必须为友元函数的形式.而且输入操作符要考虑输入失败的情况. d) 运算符重载为成员函数的形式,那么该操作符的第一个操作数必然为该类的对象

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

1.递增递减运算符 C++语言并不要求递增递减运算符必须是类的成员.但是因为他们改变的正好是所操作对象的状态,所以建议设定为成员函数. 对于递增与递减运算符来说,有前置与后置两个版本,因此,我们应该为类定义两个版本的递增与递减运算符. 问题来了,程序是如何区分前置和后置呢?因为都是++和-- 为了解决这个问题,后置版本的递增递减运算符接受一个额外的(不被使用)int类型的形参.当我们使用后置运算符时,编译器为这个形参提供一个值为0的实参.这个形参唯一的作用就是区分前置和后置运算符函数. 因为不会

c++笔记--关于箭头运算符重载使用

在stl里面,list的迭代器有重载箭头运算符.之前没搞清楚这个有什么用,看了一些资料,加上自己前面一篇笔记. 写了下面测试程序: 1 #include <iostream> 2 #include <queue> 3 #include <climits> 4 #include <algorithm> 5 #include <memory.h> 6 #include <stdio.h> 7 #include <map> 8

初探C++运算符重载学习笔记&lt;1&gt;

运算符重载 含义: 对抽象数据类型也能够直接使用C++提供的运算符 对已有的运算符赋予多重的含义 使同一运算符针对不同的数据类型产生不同的行为 目的: -->扩展C++运算符的适用范围,以用于类所表示的抽象数据类型 -->程序更简洁 -->代码可读性更好 例如complex_a和complex_b是两个复数对象,如果求和,希望能直接写成 complex_a + complex_b  这样的形式. 实质: 函数重载 返回值类型 operator 运算符(参数表) { .... } 在函数编

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

总结: 1.不能重载的运算符: . 和 .* 和 ?: 和 ::  和 sizeof 和 typeid 2.重载运算符有两种基本选择: 类的成员函数或者友元函数, 建议规则如下: 运算符 建议使用 所有一元运算符 成员函数 = () [] -> 必须是成员函数 += -= /= *= ^= &= != %= >>= <<= , 似乎带等号的都在这里了. 成员函数 所有其它二元运算符, 例如: –,+,*,/ 友元函数 3.前几篇中的实例,现在汇总Person类的程序:

初步C++运算符重载学习笔记&amp;lt;3&amp;gt; 增量递减运算符重载

初步C++运算符重载学习笔记<1> 初探C++运算符重载学习笔记<2> 重载为友元函数     增量.减量运算符++(--)分别有两种形式:前自增++i(自减--i).后自增i++(自减i--) 因此当我们重载该运算符时,要重载对应形式的运算符. T & operator ++() // 前自增++i T & operator ++(int) //后自增 i++ 举例: #include <iostream> using namespace std; c

C++ Primer 学习笔记_27_操作符重载与转换(2)--++/--运算符重载、!运算符重载、赋值运算符重载 、String类([]、 +、 += 运算符重载)、&gt;&gt;和&lt;&lt;运算符重载

C++ Primer 学习笔记_27_操作符重载与转换(2)--++/--运算符重载.!运算符重载.赋值运算符重载 .String类([]. +. += 运算符重载).>>和<<运算符重载 一.++/--运算符重载 1.前置++运算符重载 成员函数的方式重载,原型为: 函数类型 & operator++(); 友元函数的方式重载,原型为: friend 函数类型 & operator++(类类型 &); 2.后置++运算符重载 成员函数的方式重载,原型为:

C++ Primer 学习笔记_28_操作符重载与转换(3)--成员函数的重载、覆盖与隐藏、类型转换运算符、*运算符重载、-&gt;运算符重载

C++ Primer 学习笔记_28_操作符重载与转换(3)--成员函数的重载.覆盖与隐藏.类型转换运算符.*运算符重载.->运算符重载 一.成员函数的重载.覆盖与隐藏 对于类层次的同名成员函数来说,有三种关系:重载.覆盖和隐藏,理清3种关系,有助于写出高质量的代码. 1.成员函数的重载 重载的概念相对简单,只有在同一类定义中的同名成员函数才存在重载关系,主要特点时函数的参数类型和数目有所不同:但不能出现函数参数的个数和类型均相同,仅仅依靠返回值类型不同来区分的函数,这和普通函数的重载是完全一致

C++ Primer Plus学习笔记之运算符重载

C++ Primer Plus学习笔记之运算符重载 1,成员函数和友元函数选择的建议 下面我们先看两个例子: 成员函数重载 #include<iostream> using namespace std; class Complex { public: Complex(double r=0,double i=0) { re=r; im=i; } Complex operator+(const Complex& obj); Complex operator!(); void Display