• C++ 类和对象 从入门到超神(下)


    目录

    一、拷贝构造函数

    1.1 概念

    1.2 拷贝构造函数的特征

    1.3 拷贝构造函数调用场景

    1.4 指针方式的拷贝构造

    二、赋值运算符重载

    2.1 运算符重载

    2.2 赋值运算符重载  

    三、 const 成员

    四、取地址运算符重载


    一、拷贝构造函数

    1.1 概念

    在创建对象时,可否创建一个与已存在对象一模一样的新对象?

    拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般用 const 修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。


    1.2 拷贝构造函数的特征

    构造函数也是特殊的成员函数,其特征如下:

            1. 拷贝构造函数是构造函数的一个重载形式

    拷贝构造函数是构造函数的重载形式,即函数名也与类名相同,但是函数参数不同。

            2.拷贝构造函数的参数只有一个必须是类类型对象的引用,使用传值方式编译器直接报错,因为会引发无穷递归调用

    首先我们明确一件事情,因为函数传参时,形参是实参的一份临时拷贝,就会调用拷贝构造构造函数,我们来看看是如何调用

    运行结果如下:

     所以说,如果我们不使用引用传参的话,函数参数那里就会一直调用拷贝构造函数,如下图:

    因此会一直死循环,所以编译器会自动识别为语法错误。

    正确的拷贝方式:

    1. Date (const Date& d)
    2. {
    3. _year = d._year;
    4. _month = d._month;
    5. _day = d._day;
    6. }

            3. 若未显示定义,编译器会生成默认的拷贝构造函数。默认的拷贝函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或是值拷贝。

    按字节序完成拷贝,所以编译器会对基本类型和自定义类型都做处理。所以像日期类这种没有额外开辟空间,并且成员变量都为基本类型的类类型,我们完全可以使用默认的拷贝构造函数完全够用。

    但是入如果是 stack 这样需要额外开辟空间的类类型,我们使用默认的拷贝构造函数就会出现以下这种情况: 

     从上图可以了解到对拷贝生成的 st2 进行改变,会对 st1 产生影响,这就是浅拷贝的弊端,这必然会对使用产生影响,所以这种额外开辟空间的类类型我们就要自定义拷贝构造函数了。

    这里实现拷贝构造就需要自己实现深拷贝,这里不做讲解,在后面会有大量的篇幅介绍深、浅拷贝。

            4.编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝,还需要自己显示实现吗?当然想日期类这样的类是没必要的。

    注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。


    1.3 拷贝构造函数调用场景

    • 使用已存在对象创建新对象

    • 函数参数类型为类类型对象

    • 函数返回值类型为类类型对象


    1.4 指针方式的拷贝构造

    因为拷贝构造函数就是一个函数重载,所以我们不仅可以使用引用,还可以使用指针的方式,这两种方式都可以避免死循环调用拷贝构造,这种方式只是作为了解,大家了解一下就行了。

    调用方式:


    二、赋值运算符重载

    2.1 运算符重载

    C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值与参数列表与普通的函数类似。

    函数名为:关键字 operator 后面接需要重载的运算符符号.

    函数原型:返回值类型 operator 操作符 (参数列表)

    注意:

    1. 不能通过连接其他符号来创建新的操作符:比如 operaor!等。
    2. 重载操作符必须有一个类类型参数
    3. 用于内置类型的运算符,其含义不能改变,例如:内置的整形+ , 不能改变其含义
    4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的 this。
    5.  .*     ::      sizeof     ?:     .     这5个运算符不能重载。

    实现举例:

     使用效果:

     有许多种操作符,我们都可以实现,上面只是举例了 == 相等这种的运算符重载


    2.2 赋值运算符重载  

    在实现赋值运算符重载时我们要实现下面这四种要求:

    1. 赋值运算符重载格式

            ① 参数类型: const T& ,传递引用可以提高传参效率

            ② 返回值类型: T& ,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值。

            ③ 检测是否自己给自己赋值

            ④ 返回 *this :要复合连续赋值的含义

    符合标准的 赋值运算符重载的实现:

    2. 赋值运算符只能重载成类的成员函数不能重载成全局函数

            原因: 赋值运算符如果不显示实现,编译器会生成一个默认的。此时用户再在类外实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。


    3.用户没有显示实现时,编译器会生成一个默认的赋值运算符重载,以值的方式逐字节拷贝。

    注意: 

            内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。

            既然编译器生成的默认赋值运算符重载函数已经可以完成字节序的值拷贝了,还需要自己实现吗? 当然像日期类这种类是没必要的,那像下面的 Stack 栈类呢? 我们可以一起看看

    4.赋值运算符重载小总结

    赋值运算符其实就是运算符重载中的一种,它也是一个默认的成员函数。

    其中有三点需要注意

    1. 要符合标准的书写方式,即那 4 点要求
    2. 不能重载为全局函数,只能重载为类成员函数
    3. 其本质是以值拷贝的方式进行的浅拷贝,像Stack、Queue这种开辟空间的需要我们手动书写,像 Date 和 MyQueue 这些类使用默认的即可。

    三、 const 成员

    将 const 修饰的 “成员函数” 称之为 const 成员函数,const 修饰类成员函数,实际修饰该成员函数隐含的 this 指针,表明在该成员函数中不能对类的任何成员进行修改。


    四、取地址运算符重载

    这也是一个默认的成员函数,其本质也是用的运算符重载,只不过用的是 & 运算符进行的重载。

    具体实现:

     但是 & 运算符一般不需要重载,使用编译器默认生成的取地址重载即可。

     使用举例(取对象 t1 和 d1 的地址):

     

  • 相关阅读:
    oracle报错 ORA-02290: 违反检查约束条件问题
    智能护栏碰撞监测系统:强化道路安全的智能守卫
    Springboot中EasyExcel导出及校验后导入前后台功能实现
    【计算机视觉 | 目标检测】arxiv 计算机视觉关于目标检测的学术速递(8 月 30 日论文合集)
    大前端进阶
    【C++ 学习】指针与函数与多维数组
    windows下远程让linux服务器关机
    【Java 进阶篇】深入理解 JDBC:Java 数据库连接详解
    运动品牌推荐:2022年最值得入手的一些运动装备
    结尾:编程指南
  • 原文地址:https://blog.csdn.net/Brant_zero/article/details/126041179