一文搞懂C++中的运算符重载

目录
  • 引入
  • 一.运算符重载是什么
  • 二.运算符重载的格式
  • 三.部分运算符重载的实现
    • 3.1 简单‘ + ’ ‘ - ’ ‘ * ’运算符重载
    • 3.2 ++,- - 运算符
    • 3.3 =运算符
    • 3.4 <<,>>运算符
  • 四.运算符重载注意事项
  • 五.运算符重载的限制
  • 六.MyString的简单实现
    • MyString.h
    • MyString.cpp

引入

对于基本类型的常量或变量进行运算时,我们可以使用 +、-、*、/ 等运算符,但是我们不可以使用运算符来进行对象之间的运算。

eg:对象之间的加法运算

class A
{
public:
    A(int i=0,int j=0,int k=0):m_i(i),m_j(j),m_k(k){}
    int geti() //接口
    {
        return m_i;
    }
    int getj()//接口
    {
        return m_j;
    }
    int getk()//接口
    {
        return m_k;
    }
    void Add(A &b)
    {
        int i=m_i+b.m_j;
        int j=m_j+b.m_j;
        int k=m_k+b.m_k;
        cout<<i<<" "<<j<<" "<<k<<" "<<endl;
    }
private:
    int m_i;
    int m_j;
    int m_k;
};
void main()
{
    A a(6,6,6);
    A b(7,7,7);
    a.Add(b);
}

正如例子中所说:这里我们无法进行对象之间的加法,对于加法操作我们需要使用接口,然后定义add函数进行加法的操作,这样也就导致了代码十分复杂。

为了使得为了当前程序的可读性更好,简单明了。所以我们引入运算符重载这个概念。

利用 C++ 提供的“运算符重载”机制,赋予运算符新的功能,就能解决用+将两个复数对象相加这样的问题。

一.运算符重载是什么

运算符重载:就是对已有的运算符赋予多重含义,使同一运算符作用于不同类型的数据时产生不同的行为。

运算符重载的目的是使得 C++ 中的运算符也能够用来操作对象。

运算符重载的实质是编写以运算符作为名称的函数。

二.运算符重载的格式

返回值类型  operator  运算符(形参表)
{
    ....
}

这里对于返回值类型 大家可能会产生疑惑:什么是返回值类型,这里对返回值类型进行说明。

返回类型主要包括三类:

  • 值返回
  • 引用返回
  • 指针返回

值返回与引用返回 – 基本数据类型

  • 值返回:由运算符操作的表达式只能作为右值
  • 引用返回: 由运算符所操作的表达式可作为左值
  • 指针返回:有指针的相关操作
void main()
{
    int i =10;
    int j =20;
    int k =0;
    i+j=k;// error 加号的表达式只可以做右值
    //i+j有临时空间来存储表达式的值,但是没有确定的,不能把表达式的值放在i或者j空间
    //不能作为左值的原因是 没有相应的内存空间 i有自己的空间 j有自己的空间 但是i+j没有
    //右边要赋值给左边,那么左边必须要有确定的内存空间
    k = i+j;//ok
    ++i = k;//i=30
    /*
    ++i表达式的值 不管在什么时候都是i加过1之后的值,所以不用重新开辟临时空间存储表达式的值,用i的空间即可
    ++i=k;就是把k的值放在i的内存单元
    */
    i++ = j;//error
    /*
    i++的j是i没有加过的值 但是最后i还是需要+1 所以此时i和i++不是同一个内存单元 所以需要重新开辟空间存储表达式的值,不可以这样使用
    */
    (i=j)=40;//=运算符可以作为左值 即可以引用返回
}

这里告诉大家如何快速判断是值返回还是引用返回:

判断是左值还是右值

如果对左值右值区分不是很清楚,可以参考这篇博客:

链接: c++左值,右值,将忘值

三.部分运算符重载的实现

3.1 简单‘ + ’ ‘ - ’ ‘ * ’运算符重载

对于简单‘ + ’ ‘ - ’ ‘ * ’运算符重载举例如下:

#include<stdio.h>
#include<iostream>
using namespace std;
class A
{
public:
    A(int i=0):m_i(i){}
    A operator+(const A& t)//这里用const是为了本身值不改变
    {
        cout << "A(+)" << endl;
        return m_i + t.m_i;
    }
    A operator-(const A& t)//这里用const是为了本身值不改变
    {
        cout << "A(-)" << endl;
        return this->m_i-t.m_i;
    }
    A operator*(const A& t)//这里用const是为了本身值不改变
    {
        cout << "A(*)" << endl;
        return this->m_i*t.m_i;
    }
    void print()
    {
        cout << m_i << endl;
    }
private:
    int m_i;
};
void main()
{
    A a(10);
    A b(20);
    (a + b).print();
    (a - b).print();
    (a * b).print();
}

运算结果:

3.2 ++,- - 运算符

自增运算符++、自减运算符–都可以被重载,但是它们有前置、后置之分。

以++为例,假设 obj 是一个 CDemo 类的对象,++obj和obj++本应该是不一样的,前者的返回值应该是 obj 被修改后的值,而后者的返回值应该是 obj 被修改前的值。如果如下重载++运算符:

CDemo & CDemo::operator ++ ()
{
    //...
    return * this;
}

不论obj++还是++obj,都等价于obj.operator++()无法体现出差别。

为了解决这个问题,C++ 规定,在重载++或- -时,允许写一个增加了无用 int 类型形参的版本,编译器处理++或–前置的表达式时,调用参数个数正常的重载函数;处理后置表达式时,调用多出一个参数的重载函数。

对于前置运算符:左值 引用返回

对于后置运算符:右值 值返回

举例如下:

#include <iostream>
using namespace std;
class CDemo {
private:
    int n;
public:
    CDemo(int i=0):n(i) { }
    CDemo & operator++(); //用于前置形式
    CDemo operator++( int ); //用于后置形式
    operator int ( ) { return n; }
    friend CDemo & operator--(CDemo & );
    friend CDemo operator--(CDemo & ,int);
};
CDemo & CDemo::operator++()
{//前置 ++
    n ++;
    return * this;
}
CDemo CDemo::operator++(int k )
{ //后置 ++
    CDemo tmp(*this); //记录修改前的对象
    n++;
    return tmp; //返回修改前的对象
}
CDemo & operator--(CDemo & d)
{//前置--
    d.n--;
    return d;
}
CDemo operator--(CDemo & d,int)
{//后置--
    CDemo tmp(d);
    d.n --;
    return tmp;
}
int main()
{
    CDemo d(5);
    cout << (d++ ) << ","; //等价于 d.operator++(0);
    cout << d << ",";
    cout << (++d) << ","; //等价于 d.operator++();
    cout << d << endl;
    cout << (d-- ) << ","; //等价于 operator-(d,0);
    cout << d << ",";
    cout << (--d) << ","; //等价于 operator-(d);
    cout << d << endl;
    return 0;
}

3.3 =运算符

同类对象之间可以通过赋值运算符=互相赋值。如果没有经过重载,=的作用就是把左边的对象的每个成员变量都变得和右边的对象相等,即执行逐个字节拷贝的工作,这种拷贝叫作“浅拷贝”。

对于深拷贝浅拷贝可以参考这篇博客:

链接: c++拷贝构造函数

有的时候,两个对象相等,从实际应用的含义上来讲,指的并不应该是两个对象的每个字节都相同,而是有其他解释,这时就需要对=进行重载。

如果没有写=重载 呢么类会提供默认的=重载

对于=运算符举例如下:

A& operator=(const A& b)
{
    cout << "=" << endl;
    m_i = b.m_i;
    return *this;
}

3.4 <<,>>运算符

在 C++ 中,对于左移运算符<<可以和 cout 一起用于输出,因此也常被称为“输出运算符”。

实际上,对于<<来说,他本来并没有这样的功能,之所以能和 cout 一起使用,是因为被重载了。

cout 是 ostream 类的对象。ostream 类和 cout 都是在头文件 中声明的。ostream 类将<<重载为成员函数,而且重载了多次。为了使cout<<"Star War"能够成立,ostream 类需要将<<进行如下重载:

ostream & ostream::operator << (const char* s)
{
    //输出s的代码
    return * this;
}

cin 是 istream 类的对象,是在头文件 中声明的。istream 类将>>重载为成员函数,因此 cin 才能和>>连用以输入数据。一般也将>>称为“流提取运算符”或者“输入运算符”

istream & operator>>( istream & is,Complex & c)
{
    return is;
}

四.运算符重载注意事项

1.可以重载成成员和友元两种形式

对象.operator运算符(第二个运算数)–成员形式

operator运算符(按照顺序写运算数)–友元形式

2.重载成成员形式,将第一个运算数省略(当成this)

重载成友元形式,参数不能省略 。

3.一般建议赋值重载为成员(如果第一个参数是本类对象,则重载魏本类的成员形式)

[]也建议重载为成员

<< >> 重载成友元,cout<<a.左操作数是cout的类对象,不可能是本类对象,只能重载为友元。

40运算符重载不可以改变优先级,结合性,操作数个数

五.运算符重载的限制

1.不可以臆造新的运算符

2.不可以改变原有运算符的优先级,语法等特点。

//不能将求模运算符(%)重载成使用一个操作数:
int x;
Time shiva;
% x;    //无效的求模运算符
% shiva;  //无效的重载操作符

3.运算符重载不可以使用太多

4.重载运算符含义必须清楚,不能有二异性质

5.以下运算符不可以重载

6.下列运算符只能通过成员函数进行重载

  • =:赋值运算符
  • ():函数调用运算符
  • []:下标运算符
  • ->:通过指针访问类成员的运算符

六.MyString的简单实现

MyString.h

#ifndef MYSTRING_H
#define MYSTRING_H
#include<iostream>
using namespace std;
class mystring
{
    char * _str;
public:
    mystring(const char*str=nullptr);
    mystring(const mystring &another);
    mystring &operator=(const mystring&another);
    mystring operator+(const mystring&another);
    char& operator[](const int& i);
    bool  operator==(const mystring&another);
    bool  operator<(const mystring &another);
    bool  operator>(const mystring &another);
    friend ostream &operator<<(ostream&out,mystring&str);
    int size();
    ~mystring();
};

MyString.cpp

#include "mystring.h"
#include<string.h>
int mystring::size(){
    return strlen(this->_str);
}
mystring::mystring (const char* str){
    if(str==nullptr)
    {
        _str=new char[1];
        _str[0]='\0';
        return;
    }
    _str=new char[strlen(str)+1];
    strcpy(_str,str);
}
mystring::mystring(const mystring &another){

    int lenth=strlen(another._str);
    _str=new char[lenth+1];
    strcpy(_str,another._str);

}
mystring& mystring::operator=( const mystring&another){
    if(*this==another)return *this;
    else {
        delete[] _str;
        int lenth=strlen(another._str);
        _str=new char[lenth+1];
        strcpy(_str,another._str);
        return *this;
    }
}
mystring mystring:: operator+(const mystring&another){
    mystring tmp;
    delete[] tmp._str;
    int lenth=strlen(_str)+strlen(another._str);
    tmp._str=new char[lenth+1];
    memset(tmp._str,0,lenth);
    strcat(tmp._str,_str);
    strcat(tmp._str,another._str);
    return tmp;
}
char& mystring:: operator[](const int& i){
    return _str[i];
}
bool mystring:: operator==(const mystring&another){
    if( strcmp(_str,another._str)==0)
        return true;
    else return false;
}
bool  mystring:: operator<(const mystring &another){
    if( strcmp(_str,another._str)<0)
        return true;
    else return false;
}
bool  mystring:: operator>(const mystring &another){
    if( strcmp(_str,another._str)>0)
        return true;
    else return false;
}

ostream &  operator<<(ostream&out,mystring&str){
        for(int i=0;i<strlen(str._str);++i)
            out<<str._str[i];
        return out;
}

mystring:: ~mystring(){
    delete[]_str;
}

测试函数:

#include<iostream>
#include<mystring.h>
#include<string>
using namespace std;
int main(){
 mystring test1("hello");
 cout<<test1<<endl;
 cout<<test1[0]<<endl;
 mystring test2("world");
 cout<<test2<<endl;
  mystring test3=test1+test2;
  cout<<test3<<endl;
  if(test1>test2)
      cout<<test1<<" > "<<test2<<endl;
  else if(test1==test2) cout<<test1<<" = "<<test2<<endl;
  else cout<<test1<<" < "<<test2<<endl;
  return 1;
}

结果:

以上就是一文搞懂C++中的运算符重载的详细内容,更多关于C++运算符重载的资料请关注我们其它相关文章!

(0)

相关推荐

  • c++ *运算符重载

    运算符重载,对象和指向对象的指针 直接上code 复制代码 代码如下: #include <iostream> using namespace std;  class test {     public:         int a;         test() : a(0){}         test &operator*(){             cout << "operator*" << endl;             c

  • C++重载运算符你真的了解吗

    目录 1.重载运算符的必要性 2.重载运算符的形式与规则 3.重载运算符的运算 4.转义运算符 总结 运算符实际上是一个函数,所以运算符的重载实际上是函数的重载,.编译程序对运算符的重载的选择,遵循函数重载的选择原则.当遇到不很明显的运算时,编译程序会寻找与参数相匹配的运算符函数. 1.重载运算符的必要性 C++语言中的数据类型分为基本数据类型和构造数据类型.基本数据类型可以直接完成算术运算.例如: #include<bits/stdc++.h> using namespace std; in

  • C++运算符重载图文详解

    目录 1. 运算符重载 1.1 运算符重载为普通函数 1.2 运算符重载为成员函数 2. 赋值运算符=的重载 2.1浅复制与深复制 2.2返回值的讨论 3. 动态可变长度数组 总结 1. 运算符重载 C++的运算符只能用于基本的数据类型 表达形式 返回值类型 operator 运算符 (形参表) { ... } 1.1 运算符重载为普通函数 1.2 运算符重载为成员函数 2. 赋值运算符=的重载 当赋值运算符两边的类型不匹配,比如int类型赋值给Complex类型,在这种情况下,就需要重载赋值运

  • C++运算符重载的详细讲解

    加号运算符重载 对于内置数据类型,编译器知道如何运算 但是对于自己封装的类,编译器无法进行运算 这时可以通过自己定义运算符重载进行运算 operator+ 通过成员函数重载+号 #include<iostream> using namespace std; class Person { public: int m_a; int m_b; //通过成员函数实现重载 Person operator+ (Person &p) { //创建一个临时变量 Person temp; temp.m_

  • C++中运算符重载详解及其作用介绍

    目录 概述 函数重载 运算符重载 C++ 的运算符 重载运算符的规则 成员函数实现 Complex 加法 运算符重载的方法 多种实现方法 实现 operator+= 三种运算符重载函数 成员函数实现 友元函数实现 输出结果 重载单元运算符 例子 重载二元运算符 例子 重载 I/O 插入运算符 << 提取运算符 >> 总结 概述 运算符重载 (Operator Overloading) 函数重载 重载: 将同一名字重新赋予新的含义. 函数重载: 对一个函数赋予新的含义, 使之实现新功

  • C++运算符重载规则详解

    C++允许重载的运算符和不允许重载的运算符 C++中绝大部分的运算符允许重载,具体规定见表 不能重载的运算符只有5个: .  (成员访问运算符) .*  (成员指针访问运算符) ::  (域运算符) sizeof  (长度运算符) ?:  (条件运算符) 前两个运算符不能重载是为了保证访问成员的功能不能被改变,域运算符和sizeof 运算符的运算对象是类型而不是变量或一般表达式,不具备重载的特征. C++运算符重载的规则 C++对运算符重载定义了如下几条规则. 1) C++不允许用户自己定义新的

  • C++超详细讲解运算符重载

    目录 概念 赋值运算符重载 const成员 取地址及const取地址操作符重载 概念 C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似. 函数名字为:关键字operator后面接需要重载的运算符符号. 函数原型:返回值类型 operator操作符(参数列表) 需要注意的几点: 不能通过连接其他符号来创建新的操作符:比如operator@,必须是已有的操作符: 重载操作符必须有一个类类型

  • 一文搞懂C++中的运算符重载

    目录 引入 一.运算符重载是什么 二.运算符重载的格式 三.部分运算符重载的实现 3.1 简单‘ + ’ ‘ - ’ ‘ * ’运算符重载 3.2 ++,- - 运算符 3.3 =运算符 3.4 <<,>>运算符 四.运算符重载注意事项 五.运算符重载的限制 六.MyString的简单实现 MyString.h MyString.cpp 引入 对于基本类型的常量或变量进行运算时,我们可以使用 +.-.*./ 等运算符,但是我们不可以使用运算符来进行对象之间的运算. eg:对象之间的

  • 一文搞懂ES6中的Map和Set

    Map Map对象保存键值对.任何值(对象或者原始值) 都可以作为一个键或一个值.构造函数Map可以接受一个数组作为参数. Map和Object的区别 •一个Object 的键只能是字符串或者 Symbols,但一个Map 的键可以是任意值. •Map中的键值是有序的(FIFO 原则),而添加到对象中的键则不是. •Map的键值对个数可以从 size 属性获取,而 Object 的键值对个数只能手动计算. •Object 都有自己的原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突.

  • 一文搞懂Python中列表List和元组Tuple的使用

    目录 列表 List 列表是有序的 列表可以包含任意对象 通过索引访问列表元素 列表嵌套 列表可变 元组 Tuple 定义和使用元组 元素对比列表的优点 元组分配.打包和解包 List 与 Tuple 的区别 列表 List 列表是任意对象的集合,在 Python 中通过逗号分隔的对象序列括在方括号 ( [] ) 中 people_list = ['曹操', '曹丕', '甄姫', '蔡文姫'] print(people_list) ['曹操', '曹丕', '甄姫', '蔡文姫'] peopl

  • 一文搞懂Python中is和==的区别

    目录 ==比较操作符和is同一性运算符区别 哪些情况下is和==结果是完全相同的? 为什么256时相同, 而1000时不同? 结论 ==比较操作符和is同一性运算符区别 哪些情况下is和==结果是完全相同的? 结论 在Python中一切都是对象. Python中对象包含的三个基本要素,分别是:id(身份标识).type(数据类型)和value(值).对象之间比较是否相等可以用==,也可以用is. is和==都是对对象进行比较判断作用的,但对对象比较判断的内容并不相同.下面来看看具体区别在哪? i

  • 一文搞懂Java中的反射机制

    前一段时间一直忙,所以没什么时间写博客,拖了这么久,也该更新更新了.最近看到各种知识付费的推出,感觉是好事,也是坏事,好事是对知识沉淀的认可与推动,坏事是感觉很多人忙于把自己的知识变现,相对的在沉淀上做的实际还不够,我对此暂时还没有什么想法,总觉得,慢慢来,会更快一点,自己掌握好节奏就好. 好了,言归正传. 反射机制是Java中的一个很强大的特性,可以在运行时获取类的信息,比如说类的父类,接口,全部方法名及参数,全部常量和变量,可以说类在反射面前已经衣不遮体了(咳咳,这是正规车).先举一个小栗子

  • 教你一文搞懂Kotlin中的Jvm注解

    JvmOverloads 创建一个kotlin的类 class Student(val name: String, val sex: Int = 1, val age: Int = 18) 可以看出来 这个构造函数的参数是有默认值的,kotlin的特性对吧,我们在使用的时候可以方便的使用,比如: val student = Student("wuyue") val student2 = Student("wuyue", age = 18) 但是这个特性如果你用jav

  • 一文搞懂Python中pandas透视表pivot_table功能详解

    目录 一.概述 1.1 什么是透视表? 1.2 为什么要使用pivot_table? 二.如何使用pivot_table 2.1 读取数据 2.2Index 2.3Values 2.4Aggfunc 2.5Columns 一文看懂pandas的透视表pivot_table 一.概述 1.1 什么是透视表? 透视表是一种可以对数据动态排布并且分类汇总的表格格式.或许大多数人都在Excel使用过数据透视表,也体会到它的强大功能,而在pandas中它被称作pivot_table. 1.2 为什么要使用

  • 一文搞懂Python中Pandas数据合并

    目录 1.concat() 主要参数 示例 2.merge() 参数 示例 3.append() 参数 示例 4.join() 示例 数据合并是数据处理过程中的必经环节,pandas作为数据分析的利器,提供了四种常用的数据合并方式,让我们看看如何使用这些方法吧! 1.concat() concat() 可用于两个及多个 DataFrame 间行/列方向进行内联或外联拼接操作,默认对行(沿 y 轴)取并集. 使用方式 pd.concat( objs: Union[Iterable[~FrameOr

  • 一文搞懂Python中pandas透视表pivot_table功能

    目录 一.概述 1.1 什么是透视表? 1.2 为什么要使用pivot_table? 二.如何使用pivot_table 2.1 读取数据 2.2Index 2.3Values 2.4Aggfunc 2.5Columns 一文看懂pandas的透视表pivot_table 一.概述 1.1 什么是透视表? 透视表是一种可以对数据动态排布并且分类汇总的表格格式.或许大多数人都在Excel使用过数据透视表,也体会到它的强大功能,而在pandas中它被称作pivot_table. 1.2 为什么要使用

  • 一文搞懂Python中的进程,线程和协程

    目录 1.什么是并发编程 2.进程与多进程 3.线程与多线程 4.协程与多协程 5.总结 1.什么是并发编程 并发编程是实现多任务协同处理,改善系统性能的方式.Python中实现并发编程主要依靠 进程(Process):进程是计算机中的程序关于某数据集合的一次运行实例,是操作系统进行资源分配的最小单位 线程(Thread):线程被包含在进程之中,是操作系统进行程序调度执行的最小单位 协程(Coroutine):协程是用户态执行的轻量级编程模型,由单一线程内部发出控制信号进行调度 直接上一张图看看

随机推荐