运算符重载

概念

  • 运算符重载就是想法转换, 目的是简化函数调用的方式
  • 重载就是赋予新的含义, 运算符重载也是, 即同一个运算符可以有不同的功能
  • C++本身已经对一些运算符进行了重载, 同时C++允许程序员自己重载运算符
    • +号可以对不同类型(int float)的数据进行加法操作
    • << 既是移位运算符, 又可以配合cout向控制台输出数据
  • 定义一个运算符重载就像定义一个函数一样, 只不过这个函数名称以operator关键字开头
    • 返回类型 operator被重载的运算符(参数列表)

可以重载的运算符

运算符
关系运算符 == != < > <= >=
逻辑运算符 || && !
一元运算符 + - * & ++ --
位运算符 | & ~ ^ << >>
赋值运算符 = += -= *= /= %= &= |= ^= <<= >>=
内存声明与释放 new delete new[] delete[]
其他运算符 函数调用() 成员访问 -> 成员指针访问 ->* 逗号 , 下标 []

不能重载的运算

  • 点运算符 .
  • 成员指针访问运算符 *
  • 域运算符 ::
  • 长度运算符 sizeof
  • 三元运算符 ? :

注意

  • 重载不能修改运算变量的个数, 比如, 关系运算是二元运算, 重载后也必须有两个变量参数运算
  • 重载不能修改运算符的优先级别, 比如, 乘除优先于加减, 重载后这个优先级不会被修改
  • 重载不能修改运算顺序, 比如, 赋值运算是从右到左的, 重载后不能改变
class Integer
{
public:
//构造函数
Integer(): m_num(0){}
Integer(int num): m_num(num){} const Integer operator+(const Integer & other) const
{
cout << "重载了+号运算符, 以实现两个Integer的对象的相加" << endl;
return Integer(this->m_num + other.m_num);
}
private:
int m_num;
} Integer num1(1024);
Integer num2(20);
Integer num3 = num1 + num2; //编译器实际调用 num3 = num1.operator+(num2);

const的作用

  • const修饰成员变量

    • 只有一个const时, 如果const位于*的左侧, 表示指针所指的数据是常量, 不能通过该指针修改实际数据

      const int num = 1024;
      num = 2048; //不合法 //const在*号左侧, 表示指针所指向数据是常量
      const int * ptr1_num1 = &num1;
      int const * ptr2_num1 = &num;
      prt1_num = &num; //合法, 可以指向其他内存单元
      *ptr_num1 = 1234; //不合法, 不能修改所指向的数据
    • 只有一个const时, 如果const位于*的右侧, 表示指针本身是常量, 不能指向其他内存单元, 所指向的数据可以修改

      //const在*右侧, 表示指针本身是常量, 不能指向其他内存单元
      int * const ptr3_num1 = &num1;
      ptr3_num1 = ptr2_num;
    • 如果有两个const位于*的左右两侧, 表示指针和指针所指向的数据都不能修改

  • const修饰函数参数

    • const修饰引用时, 不能修改引用对象的任何成员, 可以保护传递的参数, 起到不copy对象的目的, 节省效率
  • const修饰返回值

    • 如果函数要返回局部对象, 直接返回这个对象即可, 不要返回这个对象的引用
    • 在可以返回对象, 也可以返回对象的引用时, 首选返回引用, 提高效率
  • const修饰函数,时, 说明函数不会修改成员变量的值

友元函数

  • 对于很多运算符来说, 可以选择使用成员函数或非成员函数来实现运算符重载, 通常非成员函数就是友元函数, 这样可以直接访问类的私有数据
  • 在定义运算符时, 不能同时选择这两种格式, 同时定义两种格式将被视为二义性错误, 出现编译错误

建议准则:

  • C++规定, 赋值运算符=, 数组下标运算符[], 函数调用运算符(), 成员访问运算符->, 在重载时必须声明为类的成员函数

  • 一元运算符和复合赋值运算符重载时, 一般声明为类的成员函数

  • 流运算符<< >>, 类型转换运算符不能定义为类的成员函数, 只能是友元函数

  • 二元运算符在运算符重载时, 一般声明为友元函数

firend ostream& operator<<(ostream& out, const Hero& hero);

firend ostream& operator<<(ostream& out, const Hero& hero)
{
out << "昵称" << hero.GetNickName() << '\n';
out << "等级" << hero.GetLevel() << '\n';
return out;
}

赋值/拷贝构造函数

  • 简单的类, 默认拷贝构造函数一般就构用, 不需要显式地定义一个功能类似的拷贝构造函数

  • 当类拥有其他资源时, 如动态分配的内存, 打开的文件, 指向其他数据的指针, 网络连接等, 默认拷贝构造函数就不能拷贝这些资源, 必须显式地定义拷贝构造函数, 以完整地拷贝对象的所有数据

  • 为类定义赋值/拷贝构造函数, 即以对象为参数的构造函数

    Student::Student(Student & stu_ptr)
    
    //const关键字可以保证复制过程中不会改变被复制的对象
    Student::Student(const Student & stu_ptr)
  • 下面三张场景会调用复制构造函数

    • 当类的对象被初始化为同一类的另一个对象时
    • 当对象被作为参数传递给一个函数时
    • 当函数返回一个对象时

类型转换

  • C++中, 存在隐式类型转换语法, 即自动类型转换

    int a = 12;
    a = 22.9 + a;
  • C++还提供了显式类型转换语法, 即强制类型转换

    int num = int(89.3);
  • C语言中,采用以下语法

    int num = (int)89.3;
  • 注意

    • 将浮点型数据赋值给整型变量时, 会舍弃小数部分
    • 将整型数据赋值给浮点型变量时, 数值不变, 但是会以指数形式存储
    • 将double性数据赋值给float型变量时, 注意数值范围溢出
    • 字符型数据可以赋值给整型变量, 存入的是字符的ASCII码
    • 将一个int, short或long型赋值给char类型变量时, 只将低8位传给char变量
    • 将有符号型数据赋值给长度相同的无符号型变量时, 连同原来的符号位一起传送
  • C++允许自定义类型之间转换, 但是自定义的类型转换规则只能以类的成员函数的形式出现

  • 将其他类型转换为当前类的类型时, 需要借助转换构造函数

再谈构造函数

  • 构造函数是在创建对象时, 初始化对象
  • 编译会根据传递的实参来匹配不同的构造函数
//无参构造
Rectangle(); Rectangle rect1(); //带参构造
Rectangle(float width, float height); Rectangle rect2(20, 30); //拷贝构造, 以拷贝的方式初始化对象时调用
Rectangle(const Rectangle& rect); Rectangle rect3(rect2); //转换构造, 将其他类型转换为当前类型时调用
Rectangle(float width); Rectangle rect4(99.8);
Rectangle rect5 = 66.6;
Rectangle rect6;
rect6 = rect5 + 'A' + false; //将char, bool都转换为Rectangle类型再运算
  • 当前类的类型转换为其他类型时, 使用类型转换函数

  • 类型转换函数只能以成员函数的形式出现, 也就是只能出现在类中

    //类型转换函数的语法格式
    operator type()
    {
    return data;
    } /*
    type可以是内置类型, 类类型以及由typedef定义的类型别名, 任何作为函数返回类型的类型都是被支持的, void除外, 且不允许转换为数组或函数类型, 可以转换为指针或引用类型
    */
  • 类型转换函数看起来没有返回值类型, 但其实隐式地指明了返回值类型

  • 类型转换函数也没有参数, 因为要将当前类的对象转换为其他类型, 编译器实际上会把当前对象的地址赋值给this指针, 这样就可以在函数体内可以操作当前对象

    operator float() const{
    return this->width;
    } //类型转换函数一般不会更改被转换的对象, 所以通常被定义为const
    //类型转换函数可以被继承, 可以是虚函数

C++---使用类的更多相关文章

  1. Java类的继承与多态特性-入门笔记

    相信对于继承和多态的概念性我就不在怎么解释啦!不管你是.Net还是Java面向对象编程都是比不缺少一堂课~~Net如此Java亦也有同样的思想成分包含其中. 继承,多态,封装是Java面向对象的3大特 ...

  2. C++ 可配置的类工厂

    项目中常用到工厂模式,工厂模式可以把创建对象的具体细节封装到Create函数中,减少重复代码,增强可读和可维护性.传统的工厂实现如下: class Widget { public: virtual i ...

  3. Android请求网络共通类——Hi_博客 Android App 开发笔记

    今天 ,来分享一下 ,一个博客App的开发过程,以前也没开发过这种类型App 的经验,求大神们轻点喷. 首先我们要创建一个Andriod 项目 因为要从网络请求数据所以我们先来一个请求网络的共通类. ...

  4. ASP.NET MVC with Entity Framework and CSS一书翻译系列文章之第二章:利用模型类创建视图、控制器和数据库

    在这一章中,我们将直接进入项目,并且为产品和分类添加一些基本的模型类.我们将在Entity Framework的代码优先模式下,利用这些模型类创建一个数据库.我们还将学习如何在代码中创建数据库上下文类 ...

  5. ASP.NET Core 折腾笔记二:自己写个完整的Cache缓存类来支持.NET Core

    背景: 1:.NET Core 已经没System.Web,也木有了HttpRuntime.Cache,因此,该空间下Cache也木有了. 2:.NET Core 有新的Memory Cache提供, ...

  6. .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类

    .NET Core中间件的注册和管道的构建(2)---- 用UseMiddleware扩展方法注册中间件类 0x00 为什么要引入扩展方法 有的中间件功能比较简单,有的则比较复杂,并且依赖其它组件.除 ...

  7. Java基础Map接口+Collections工具类

    1.Map中我们主要讲两个接口 HashMap  与   LinkedHashMap (1)其中LinkedHashMap是有序的  怎么存怎么取出来 我们讲一下Map的增删改查功能: /* * Ma ...

  8. PHP-解析验证码类--学习笔记

    1.开始 在 网上看到使用PHP写的ValidateCode生成验证码码类,感觉不错,特拿来分析学习一下. 2.类图 3.验证码类部分代码 3.1  定义变量 //随机因子 private $char ...

  9. C# 多种方式发送邮件(附帮助类)

    因项目业务需要,需要做一个发送邮件功能,查了下资料,整了整,汇总如下,亲测可用- QQ邮箱发送邮件 #region 发送邮箱 try { MailMessage mail = new MailMess ...

  10. .NET平台开源项目速览(18)C#平台JSON实体类生成器JSON C# Class Generator

    去年,我在一篇文章用原始方法解析复杂字符串,json一定要用JsonMapper么?中介绍了简单的JSON解析的问题,那种方法在当时的环境是非常方便的,因为不需要生成实体类,结构很容易解析.但随着业务 ...

随机推荐

  1. tensorflow源码解析之framework-node

    目录 什么是node node_def 关系图 涉及的文件 迭代记录 1. 什么是node TF中的计算图由节点组成,每个节点包含了一个操作,表示这个节点的作用,比如,如果一个节点的作用是做矩阵乘法, ...

  2. 02 基础 卸载JDK 安装JDK Java程序运行机制

    基础 JDK:Java Development Kit(Java开发者工具 包含JRE和JVM) JRE:Java Runtime Environment(java运行时环境,包含JVM) JVM:J ...

  3. LGP5430题解

    新的 \(O(k+\log n)\) 做法. 考虑计算每个猴子对答案的贡献. 打个表: 1 1 2 4 8 16 32 ... 可以看出第 $ i $ 个猴子对答案的贡献是 \(i^k \times ...

  4. 单链表上的一系列操作(基于c语言)

    单链表的实现分为两种单链表(其实差别并不是很大):带头结点和不带头结点,分别对应下面图中的上下两种. 链表的每一个结点是由两个域组成:数据域和指针域,分别存放所含数据和下一个结点的地址(这都是很明白的 ...

  5. Java案例——ArrayList存储学生对象并遍历

    package ArrayListDemo;import java.util.ArrayList;import java.util.Scanner;/*案例:存储学生对象并遍历 需求:创建一个存储学生 ...

  6. DDOS反射攻击

    0x01 环境 包含3台主机 attact 作为攻击方,使用Centos7.2 reflect 作为流量放大器,安装有dns .ntp .memcached三种可以放大流量的服务 windows_se ...

  7. HTTP1.0和HTTP1.1和HTTP2.0的区别

    1 HTTP1.0和HTTP1.1的区别1.1 长连接(Persistent Connection)       HTTP1.1支持长连接和请求的流水线处理,在一个TCP连接上可以传送多个HTTP请求 ...

  8. 关于Oracle数据库的PIVOT分组函数的使用

    官方文档挺详细的,在新功能那里有介绍到:http://www.oracle-developer.net/display.php?id=506 PIVOT的语法:https://docs.oracle. ...

  9. IDEA 错误:程序包XXX不存在

    第一种情况是:JDK版本不对,需要确认是否一致 第二种情况是:确认一下此菜单项是否启用Enabled 第三种情况:确认包目录是否标识为Java源目录 第四种情况:如果使用的是Gradle,确认以下配置 ...

  10. JQuery Validate验证插件自定义验证消息

    // 自定义验证的方法,验证通过返回true,否则返回false(会显示错误消息) jQuery.validator.addMethod; // 定义验证的消息 jQuery.validator.fo ...