C++基础知识-Day5
今天主要讲的是类的扩展
1.类成员函数的存储方式
首先我们介绍类成员函数的存储方式,C++引入面向对象的概念之后,C语言中的一些比如static/const等原有语义,作一些升级,此时既要保持兼容,还要保持冲突不变。一个对象所占的空间大小只取决于该对象中数据成员所占的空间,而与成员函数无关,但是对于对于一个类来说,加入其有十个对象,那么其类成员的存储方式有两种方式:每个对象具有一个函数成员、多个对象共享同一个公有函数,下面我们分别对其进行分析
第一种情况:
假设一个类定义了十个对象,那么就需要分别对10个对象和函数代码分配存储单元
理论上说,每个对象有自己的数据和函数段是可以的,但是这样会耗费大量的空间,因此我们思考能否只用一段空间来存放这个共同的函数代码段,在调用对象的函数时,都去调用这个公用的函数代码
显然,这样做会大大节约存储空间
那么就会出现疑问:当对象调用公用函数时,如何确定访问的成员是调用对象的成员呢?-利用this指针
C++设置了this指针,对象在调用公用函数时,并将对象的指针作为隐含参数传入其内,因此在对象1调用公用代码时,将对象1的地址传入公用代码
类的成员函数只有一个,在对象调用过程中,传入对象的地址,具体可以表现为this
3. const在类中的扩展(无论const修饰什么,都是不能修改的)
(1) const修饰数据成员,成为数据成员,可能被普通成员函数和常成员函数来使用,不可更改
(2) 必须初始化,可以在类中(不推荐),或初始化参数列表中(这是在类对象生成之前唯一一次改变const成员的值的机会了)
(3) 初始化列表,一方面提高了效率,另一方面解决一类因面向对象引入的一些新功能的特殊安排
4. const应用
(1) 见下面代码
这样直接进行编译会报错,报错的原因是const修饰的x没有初始化,使用的是系统默认的初始化,其肯定不会对x进行初始化的,但是如果直接将const去掉即不会报错,因此通过对比我们可以知道,const修饰类数据成员时,必须要初始化
那么,既然const必须要进行初始化,那么初始化const的方式又有哪些呢
一种情况:直接在类内部进行初始化直接const int x=100;
另一种情况:初始化列表initial list,因此有const的数据成员常需要构造器
使用初始化列表的原因,一方面是效率的问题,另一方面是为了一些新扩展的功能提供一个解决场所或者办法
(3)如果含有引用的话
相当于中间产生了一个临时变量zz,
(4) 也可以使用传参的方式,但是只能够对其进行一次的修改
const修饰的数据成员,可以在非const 函数中使用,但是不可以更改
(5) const修饰类函数成员,不可以修饰全局函数
const有几个放置的位置
const void foo(), void const foo()->这两种方式都是修饰返回值的
void foo(const int x)->这种方式是修饰参数的
void foo() const->修饰函数
(6) const构成的重载问题->其修饰函数时可以构成重载,重载函数是根据语境来确定哪个函数被调用,const构成的重载
非const对象:优先调用非const版本,在没有非const版本的时候,也可以调用const版本
const对象,只能调用const版本,很多库常见提供两个版本
如上,首先调用的是非const版本
(7) const修饰函数以后,承诺不改变,在本函数不会发生,改变数据成员的行为,只能调用const成员函数
在const修饰的函数中发生了改变数据成员的行为,因此是不可以的
(8) inline const static,声明关键字或者说是定义关键字
(9) const小结
- const修饰函数,在声明之后,实现体之前
- const函数只能调用const函数,非const函数可以调用const函数
- 如果const构成函数重载,const对象只能调用const函数,非const对象优先调用非const函数
- 类体外定义的const成员函数,在定义和声明处都需要修饰const修饰符,有些关键字是定义型的,有些关键子是声明
(10) const修饰对象:
const修饰的对象,其内可以有非const数据成员,但不可修改,只能调用const成员函数
针对const有可能修饰对象,往往提供两个版本,构成重载
5. static在类中的扩展
static可以修饰局部和全局,修饰局部变量,扩展其生命周期和存储位置
修饰全部变量,本身全局变量有外延性,加了static就只能仅限于本文件使用
C++扩展了static在类中的语义,用于实现在一个类,多个对象中共享数据,协调行为的目的。
静态变量有全局变量的优势,又不会像全局变量一样被滥用,而用于管理静态变量,就需要用到静态函数
类的静态成员,属于类,也属于对象,但终归属于类
(1) static修饰数据成员
- static修饰数据成员,需要初始化才能使用,不可以类内初始化,必须类外初始化,需要类名空间,且不需static
- 类的声明与实现分开的时候,初始化在.h还是在.cpp中?.cpp中,在实际书写过程中我们应该将其初始化写在.cpp中,类的声明之前
- static的大小,添加static之后会不会占用类的大小呢?static声明的数据成员,不占用类对象的大小,其存储在data段的rw段_m, _n, _share是一个整体,命名空间即类名是维系这个整体的基础
- 访问。 _m,_n 依赖于对象,对象生成了才可以访问 _share 不依赖与对象,在对象生成之前就已经存在了
- 通过以上代码段我们可以得出,
static既可以通过对象访问,也可以不通过对象,直接通过类名访问
- static修饰数据成员总结
- 共享:static成员变量实现了同族类对象间信息共享
- 初始化:static成员使用时必须初始化,且只能类外初始化,声明与实现分离时,只能初始化在实现部分(.cpp)
- 类大小:static成员类外存储,求类大小,并不包含在内
- 存储:static成员是命名空间属于类的全局变量,存储在修饰函数成员data区的rw区
- 访问:可以通过类名访问(无对象生成时亦可),也可以通过对象访问
(2)static修饰数据成员
以下几个演变过程
但是这样是很依赖于对象的,如果将cout<<A::fooCount<<endl写在大括号外面,则是不成立的,为了不依赖于对象,我们可以加一个函数访问其私有成员
为了使函数不依赖于对象,可以将invokefooCount函数设置为static,这样就可以直接将m. invokefooCount()改成A:: invokefooCount(),这样就可以实现函数不依赖于对象
static修饰函数,目的是为了管理静态变量
(3) static应用:一塔湖图,共享图书馆内的书籍
首先我们将需要共享的变量_lib写成公有变量,利用static关键字使其成为共享的变量
但是对于数据成员_lib一直暴露在外面使得整个函数的封装性不是很好,为了解决这个问题,我们将_lib写成私有成员变量,并且利用&getLib()函数来访问_lib
(4) 由上可以看见,static修饰成员函数,主要用来管理静态变量,类内定义需要加static ,类外定义不需要加static
(5) 静态成员函数只能访问静态的成员(数据成员和函数成员),不能访问非静态的成员
这种情况是会报错的,是因为静态函数只能访问静态数据成员而不能访问非数据成员,
发生这种情况的原因:对于静态函数,有两种情况,可以直接通过对象去访问,也可以通过类去访问,但是通过类去访问的话由于没有类,因此就不会有this指针,但是通过对象去访问的话是有this指针的,这样就会存在矛盾,所以static函数是没有this指针的,但是普通函数是有this指针的,因此static函数是不可以访问非static函数的
(6) 非静态函数是可以访问静态成员的
(7) static函数的应用:取号服务
(8) 静态函数小结
a. 静态成员函数的意义,不在于信息共享,数据沟通,而在于管理静态数据成员,完成对静态数据成员的封装
b. static修改成员函数,仅出现在声明处
c. 静态成员函数只能访问静态数据成员,原因:非静态成员函数,在调用时this指针被当作参数传递,而静态成员函数属于类,而不属于对象,没有this指针
(9) 课堂实战:单例模式:一个类仅有一个实例的形式,实现共享用的
为了实现单例模式,我们首先将构造器私有化,此时不能通过常规的手段生成对象,因此拷贝构造器也是私有化,防止生成一个单例之后利用拷贝构造器生成新的对象
最终打印出来ps和ps2的地址是一样的,因此我们可以得出,使用单例模式只能得到相同的地址
在内存管理里面,有下面几种情况
见new见delete
见new不见delete
不见new见delete
不见new 不见delete
(10) 课堂实战:Render Tree渲染树
一套成熟的类库,通常都会引入内存管理,从使用的角度来说,只见new不见delete,或是自始至终见不到new和delete
比如说cocos中渲染树,就是一种内存管理手段,对象只管生成,参与渲染和销毁由渲染树来管理,今日实战的目的:每生成一个对象,将其挂在一个链表上,最终我们能够访问这个链表
要实现这个功能,所有生成的对象肯定是共享表头的,整个渲染树的步骤为:对象的创建,初始化,挂到树上去
autoRelease的思想,在所有对象都没有存在的时候,就已经存在了head,首先生成了节点A,那么head->A,随后生成了节点B,那么新生成的节点仍然满足:让新来的节点有所指向,那么首先是节点B指向节点A,然后是head的next指向节点B,所以说还需要一个next,创建对象A之后,有一个this指针指向A
那么这一段代码一共有两种情况,一种情况是当创建一个对象之后,发现head是空的,那么只需要将head指向A的this指针,并且将A的next置空
另一种情况是当创建一个对象之后,head是不为空的,那么需要使新来的节点有所指向,此时this是指向的对象B,this->next=head;head=this
但是我们细心的可以发现,如果将if内部的语句调换一下顺序,那么if和else内部的语句格式都是一样的,问题的关键就在于head,当head=nullptr的时候,就将this->next置空,那么直接将this=head;优化后的版本如下
最终渲染树的代码为
(12) static const初始化
static const int a; 其中static和const都做a的定语,其中stati更重要,其存在于data段的ro段,因此整个初始化的方法为static const int a=100;
6. 指向类成员的指针
C++扩展了指针在类中的使用,使其可以指向类成员(数据成员和函数成员),这种行为是类层面的,而不是对象层面的
(1) C语言中的指针
(2) C++中的指针
定义一个指针,指向类的成员,不是指向对象的成员
下面讲的指针,是指类层面的指针,而不是对象层面的
在C++中,为了实现指向类的指针,我们常采用如下方法
(3) Pointer to func member
定义一个指向非静态成员函数的指针必须在三个方面与指向的成员函数保持一致:参数列表要相同,返回类型要相同、所属类型要相同
由于类不是运行时存在的对象,因此,在使用这类指针时,需要首先指定类的一个对象,然后,通过对象来引用指针所指向的成员
在(s.*pdis)(1)中,刚开始写成的是s.pdis(1),但是由于.*和->*的级别都不是很高,因此加个括号才是正确的
Pointer应用:提供更加隐蔽的借口
7. Pointer本质:
假设上述是一个类,该类中有a,b,c三个私有成员变量,分别占用的字节数为:1,4,8,则a的起始地址为0,b的起始地址为1,c的起始地址为5,假设有一个对象,其访问形式为:cout<<obj.*pointer<<endl;对象本身是有一个地址的,若指针指向的是a,则在obj的地址的基础上加0,若指针指向的是b,则在obj的地址的基础上加1,以此类推
C++基础知识-Day5的更多相关文章
- python基础-基础知识(包括:函数递归等知识)
老男孩 Python 基础知识练习(三) 1.列举布尔值为 False 的值空,None,0, False, ", [], {}, () 2.写函数:根据范围获取其中 3 和 7 整除的所有 ...
- .NET面试题系列[1] - .NET框架基础知识(1)
很明显,CLS是CTS的一个子集,而且是最小的子集. - 张子阳 .NET框架基础知识(1) 参考资料: http://www.tracefact.net/CLR-and-Framework/DotN ...
- RabbitMQ基础知识
RabbitMQ基础知识 一.背景 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然 ...
- Java基础知识(壹)
写在前面的话 这篇博客,是很早之前自己的学习Java基础知识的,所记录的内容,仅仅是当时学习的一个总结随笔.现在分享出来,希望能帮助大家,如有不足的,希望大家支出. 后续会继续分享基础知识手记.希望能 ...
- selenium自动化基础知识
什么是自动化测试? 自动化测试分为:功能自动化和性能自动化 功能自动化即使用计算机通过编码的方式来替代手工测试,完成一些重复性比较高的测试,解放测试人员的测试压力.同时,如果系统有不份模块更改后,只要 ...
- [SQL] SQL 基础知识梳理(一)- 数据库与 SQL
SQL 基础知识梳理(一)- 数据库与 SQL [博主]反骨仔 [原文地址]http://www.cnblogs.com/liqingwen/p/5902856.html 目录 What's 数据库 ...
- [SQL] SQL 基础知识梳理(二) - 查询基础
SQL 基础知识梳理(二) - 查询基础 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5904824.html 序 这是<SQL 基础知识梳理( ...
- [SQL] SQL 基础知识梳理(三) - 聚合和排序
SQL 基础知识梳理(三) - 聚合和排序 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5926689.html 序 这是<SQL 基础知识梳理 ...
- [SQL] SQL 基础知识梳理(四) - 数据更新
SQL 基础知识梳理(四) - 数据更新 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5929786.html 序 这是<SQL 基础知识梳理( ...
- [SQL] SQL 基础知识梳理(五) - 复杂查询
SQL 基础知识梳理(五) - 复杂查询 [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5939796.html 序 这是<SQL 基础知识梳理( ...
随机推荐
- 十分钟了结MySQL information_schema
information_schema数据库是MySQL系统自带的数据库,它提供了数据库元数据的访问方式.感觉information_schema就像是MySQL实例的一个百科全书,记录了数据库当中大部 ...
- 如何安装或卸载Lodop、C-Lodop
安装:下载.exe安装文件,一步步安装就行,如不特意拦截,应该是100%可以安装成功.客户端本地打印角色等,直接一步步安装就行,如果是广域网AO打印那种,在服务器上安装的c-lodop,需要勾选服务器 ...
- Essential Phone刷机教程
安装fastboot驱动(Essential-PH1-WindowsDrivers) 下载ADB刷机指令工具:platform-tools(ADB): 进入开发者选项,打开 USB 调试,OEM解锁选 ...
- 学习 Spring (六) 自动装配
Spring入门篇 学习笔记 No: (默认)不做任何操作 byName: 根据属性名自动装配.此选项将检查容器并根据名字查找与属性完全一致的 bean,并将其与属性自动装配 byType: 如果容器 ...
- ZIP压缩包加密破解
python多线程破解zip文件,废话不多说直接上代码 # -*- coding: UTF-8 -*- #使用多线程和接受参数的形式去破解指定的zip文件 #python3 zip_file_cack ...
- BZOJ5418[Noi2018]屠龙勇士——exgcd+扩展CRT+set
题目链接: [Noi2018]屠龙勇士 题目大意:有$n$条龙和初始$m$个武器,每个武器有一个攻击力$t_{i}$,每条龙有一个初始血量$a_{i}$和一个回复值$p_{i}$(即只要血量为负数就一 ...
- HDU3966-Aragorn's Story-树链剖分-点权
很模板的树链剖分题 注意什么时候用线段树上的标号,什么时候用点的标号. #pragma comment(linker, "/STACK:102400000,102400000") ...
- Fence Repair POJ - 3253 哈夫曼思想 优先队列
题意:给出一段无限长的棍子,切一刀需要的代价是棍子的总长,例如21切一刀 变成什么长度 都是代价21 列如7切成5 和2 也是代价7题解:可以利用霍夫曼编码的思想 短的棍子就放在底层 长的尽量切少一次 ...
- 洛谷P2740 草地排水
最大流 一道完全符合最大流定义的板子题..重新学了一次网络流,希望有更深的理解把.. #include <bits/stdc++.h> #define INF 0x3f3f3f3f #de ...
- [USACO12MAR] 花盆Flowerpot
类型:二分+单调队列 传送门:>Here< 题意:给出$N$个点的坐标,要求根据$x$轴选定一段区间$[L,R]$,使得其中的点的最大与最小的$y$值之差$\geq D$.求$Min\{R ...