| |
|
开发:
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++类与对象再探 |
关键字:const成员函数,构造函数,析构函数,拷贝构造函数,赋值操作符重载,取地址重载,const取地址重载。 目录 编译器如何编译类与我们所熟知的编译过程不同,编译器对于类的编译是有选择性,有顺序的。
const成员函数在C语言中有一个神奇的关键字,const关键字。 它用以给其所修饰的变量赋以常性,即:
关于const的各种各样的用法在这里就不细讲了,有时间的话单独总结一篇博客(flag + 1)。 这里我们只讨论const修饰类的成员函数。 如上所言,const最重要的特征在于——常性。我们可以简单地理解为与常量相似的特性,不可改变。
特性
?通过对代码的编译,我们可以得到如下报错: ?为什么不可以执行这样的转换?我们可以从权限的角度去理解。 const date 是一个常对象,它在被创建出来后,便只具有读的权限。 date& 则不一样,它可以对变量进行修改,是具有读与写两种权限的。 具有读写权限的可以操作只需要读权限的,只有读权限的无法操作需要读写两种权限的。 这叫做前置科技不满足。
?这仔细想想实际上是个伪命题,因为const成员函数的隐含参数this的类型是const 类类型指针,而非const成员函数的隐含参数this的类型是类类型指针。 但从这一点看两者的参数列表间永远差着一个const。 但那时隐含的,我们只关心我们在括号中看到的参数列表,所以,得出了const成员函数可以被拥有相同参数列表的非const成员函数重载这样的结论。 只是看起来如此罢了。
?几个小问题根据上文的权限的说法,很容易就可以判断出下面几个小问题的答案。
读 操作 读写 不可以。
读写 操作 读 可以。
读 操作 读写 不可以。
读写 操作 读 可以。 6个默认成员函数所谓默认成员函数,是在用户不定义的情况下,编译器会按照其规则自动生成,而如果用户进行显式了定义,编译器便不会再生成的成员函数。 构造函数概念
特性与我们所熟知的构造的含义不同,构造函数的主要任务并不是开辟空间创建对象,而是初始化对象 构造函数的特征有以下几点:
在以上情况下,编译器是会进行报错的: 可见,当我们显示定义构造函数之后,编译器便不会再生成构造函数。
分别根据两个构造函数,创建两个对象,可以看到程序成功运行: 所以,构造函数可以重载。
由这一点可以看出,默认构造函数是为了我们可以在如:
这样的情况下不会报错而设置的。 默认构造函数为什么只能有一个?这其实是很容易理解的。 默认构造函数是需要可以无参调用的。 全缺省的函数调用时是可以不输入任何参数的,而无参函数也是不输入任何参数就可以调用的。同一个类的构造函数,其必然遵循构造函数的原则——函数名为类名。 同名,构成重载,都可以不传参调用。同时存在的情况下必然会导致二义性。 一般情况下,在显式定义构造函数的时候,最好定义一个全缺省的默认构造函数。 编译器生成的默认构造函数在我们不显式实现构造函数的时候,我们创建一个对象,根据上边说到的特性,我们知道,对象此时会调用编译器自动生成的无参构造函数,但当我们直接输出对象的值的时候,会发现此时对象中成员变量的值依旧是随机值,那么这个构造函数都做了什么?
看这样一段代码,运行的结果: 我们在创建date类类型的对象时,会创建一个Time类类型的对象作为成员变量。通过运行结果可以看出Time类的构造函数被调用了。但我们创建的明明是date类的对象,那么Time类的构造函数是谁调用的? 答案是显而易见的,我们说过,构造函数是用来对对象进行初始化的,而编译器对于int等内置类型的初始化都是非常简单的放一个随机值——并不是我们不直接写明什么int i = 多少编译器就不给变量初始化了,初始化是分有默认初始化与值初始化两种的,当我们不指定初值的时候,编译器会对内置变量进行默认初始化的。
而对于如类一般的自定义类型的变量,编译器往往会调用它自身的默认构造函数。 ?注意,这是编译器自动产生的默认构造函数的一部分,我们从中很容易找到在这之中调用了Time类的构造函数。 简单地总结一下:
析构函数概念构造函数与创建对象并没有多大关系,同理,析构函数与销毁对象也没有多大关系。
这里所谓的资源清理工作,?通常指的是:堆上申请的空间,文件指针,套接字... 这些资源是从计算机中申请的,需要用户手动释放的。如果申请而不释放,则会导致资源的浪费,轻则造成计算机卡顿,重则程序崩溃。 如果我们在类的构造函数中申请了这些资源,每次在对象销毁前都手动将这些资源释放显然不是一个聪明的办法,所以C++引入了析构函数的操作,当对象销毁时,编译器都会自动调用析构函数执行其中相关的清理资源的代码(当然要用户自己写,默认生成的你敢信?)。 特征析构函数的特征有以下几点:
? ?可以看到,在do...while()循环体中创建了一个date类类型对象,同时调用了date类的构造函数,当循环结束时,编译器自动调用了析构函数~date() 析构函数的存在极大地提高了C++代码的容错率,减少了一些由于写代码时马虎不注意导致的资源浪费。 编译器自动生成的析构函数
与编译器生成的默认构造函数类似,编译器生成的默认析构函数可以调用其成员变量中自定义类型对象对应的析构函数。但如果其成员变量都是内置类型的变量,那么编译器生成的默认析构函数就没有太大的用途了。
拷贝构造函数概念
特性拷贝构造函数有以下特征:
这个原理是很简单的,传值传参,传进去的形参是实参的拷贝。? 而实现对象拷贝功能的函数正是我们这里正在讲的拷贝构造函数……然后,就递归了。 我之前针对拷贝构造函数专门写过一篇博客,有兴趣的朋友可以看一下。
赋值运算符重载运算符重载为了增强代码的可读性,C++引入了运算符重载,赋予运算符新的功能。
赋值运算符重载所谓赋值运算符,就是我们都熟知的 “ = ”运算符。我们可以为其简单定义一些功能:
这里需要再重复一下我们上方运算符重载的注意事项:尽可能地保证每个运算符重载后的功能与其对内置类型变量的操作一致。 虽然有些更改操作不会对程序运行造成什么影响,但是标新立异的后果是程序的可维护性与可读性都将大幅度降低,得不偿失。
与构造函数与析构函数相同,编译器默认生成的赋值运算符并不包含对于需要用户手动申请释放的资源的操作。 编译器自动生成的赋值运算符重载只是简单的讲这片内存的值拷贝到了那片内存,比如对象1在堆上申请的空间,如果我们没有显式定义赋值运算符重载,当我们将1的值赋给2的时候,编译器会调用其自己自动生成的赋值运算符重载的功能,而它所做的,仅仅是将1的栈帧上的变量的值拷贝到了2的栈帧上——这意味着它只是将指向堆上那片空间的指针的值拷贝了过来。对象1与对象2所操作的实际上是堆上的同一块空间。 ?且不说这样的情况下会对代码中的逻辑计算结果有什么影响,从其它角度来看,可能会导致的后果有:
?所以说,当类中涉及到资源管理时,赋值运算符一定要由用户重载。 注意事项:
?进一步了解赋值运算符重载在对赋值运算符重载的时候,往往需要注意以下四点: 1.参数类型 2.返回值类型 3.检测是否自己给自己赋值 4.返回*this 让我们根据代码示例来分析要点:
参数类型采用传引用或者传值对于结果来说,影响不大,但是,传引用的高效率却无法不令我们优先考虑。
运算符重载需要保证其含义不改变。对于内置类型变量而言,赋值运算符是可以进行连续赋值的
?同时,我们还需要知道,我们针对类类型对象所写的赋值语句,与赋值运算符的重载有着如下关系:
所以,对于连续赋值的情况:
虽然赋值运算符重载函数是对象的成员函数,在函数体内对成员变量的修改是通过隐式传入的this指针完成的,所以,即使没有返回值,单次的赋值也是没有问题的。 但问题在于,连续赋值的情况下,我们是将d2.operator(d3)的值赋给d1,如果重载函数的返回值类型为void,那么d1.operator(void)显然是没有对应的可用重载的,势必会报错。 同时,由于返回值不是引用类型时,返回值是右值,从而导致在某些情况下无法做到连续赋值的功能,所以我们将函数的返回值设置为类类型引用。 由于返回的是我们this指针所指向的对象,所以也不存在访问非法空间的问题。
同样是为了效率,在大型项目中,我们很难保证不会出现使用对象1的引用给对象1赋值的情况,这样的情况明显会对效率造成一些浪费。 所谓积水成渊,从任何可能的代码处节省时间应该称为我们所必备的意识。
?取地址运算符重载&&const取地址运算符重载
这两者我实在想不出有什么特殊的情况需要对它进行什么修改的。 实践中也是如此,大部分博客中都认为这两个操作符一般不用用户来定义重载,使用编译器默认生成的取地址重载即可。 |
|
C++知识库 最新文章 |
【C++】友元、嵌套类、异常、RTTI、类型转换 |
通讯录的思路与实现(C语言) |
C++PrimerPlus 第七章 函数-C++的编程模块( |
Problem C: 算法9-9~9-12:平衡二叉树的基本 |
MSVC C++ UTF-8编程 |
C++进阶 多态原理 |
简单string类c++实现 |
我的年度总结 |
【C语言】以深厚地基筑伟岸高楼-基础篇(六 |
c语言常见错误合集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
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年11日历 | -2024/11/23 21:52:58- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |