当前课程知识点:基于Linux的C++ > 第九讲 类与对象 > 9.6 类与对象的成员(一) > LinuxCPP0906
接下来一节是类与对象的成员
这一节的内容比较多 总共包括7个主题
一个是内联函数 一个是常数据成员
第三个是常成员函数
第四个和第五个
是静态数据成员和静态成员函数
第六个是静态常数据成员
最后一个是友元函数与友元类
我们首先来看内联函数
内联函数的最主要的目的是用于程序优化
展开函数的代码
而不是进行直接的函数调用
就是说 如果我有一个函数
它代码量其实很短
如果你每次都调用这个函数
因为函数调用是需要开销的
所以实际上在程序运行的时候会很不经济
从执行时间 也就是效率这个角度来讲
它实际上是调用函数
还不如直接编写函数体内部的代码
直接执行它 内联函数就做这个事情
它就将这段代码直接展开到
那个函数调用的地方 不进行函数调用
从而提高了我们的程序效率
但使用内联函数的时候
有几点必须要特别注意
第一个 定义这个内联函数
必须在函数的定义前添加inline
这样的一个关键字
因为inline本身
只和这个函数的定义有关
和它的原型其实没关系
所以inline这个函数
是写在这个函数的定义的前面的
这是需要特别注意的一个地方
第二个 编译器必须能看见
内联函数的代码 才能替你把内联函数
展开到那个函数调用的位置
这也就意味着 在大多数情况下边
你的内联函数是必须写在头文件里的
你的一个工程项目包括很多个文件
如果其它的源文件要使用这个内联函数
你要保证这个代码能够展开到
另外一个文件里面去
那么这个内联函数的实现
你必须写在头文件里
只有这样 它包含头文件
它才能够展开这段代码
如果你写到这个源文件里
它是不知道这段代码的
所以它展开不了 所以正常情况下
内联函数必须写在头文件里 这是第二个
第三个 要在类定义中给出函数体的时候
你比如说你在类的定义的里边
实现一个函数
你不需要写它的函数原型——后面不是分号
而是直接跟着花括号体
把它的函数实现代码都写在类里边了
你前面不用写inline 它也是inline
这是一个主要的原则
接下来一个 函数体类
如果代码量很大 而且它包括着循环
那么不要使用内联 特别注意这一点
因为你函数代码体量很大
你使用内联的效果
它会导致我们整个程序的
代码空间急剧膨胀
你程序效率可能是经济的
但是整个程序的空间是不经济的
所以并不是一个好的选择
第五个 构造函数和析构函数有可能
隐含着一些附加的操作
因此大部分情况下边不建议写内联函数
使用内联的时候要慎重
最后一个 对于编译器来说
内联函数仅仅是一个建议
你在一个函数实现前面写上inline
那就表示你建议
编译器将这个函数实现为内联函数
编译器听不听你的
它自己有自主决定权
如果一个函数代码量很大
它认为把它作为内联函数不合适
你前面写了inline 它也不会inline
这个地方是需要特别注意的一个地方
我们看这样一个例子
GetOrigin、SetOrigin这两个成员函数
代码量都很短
就两条简单的赋值语句
那么如果我们在实现的时候
每次GetOrigin、SetOrigin都是进行函数调用
实际上函数调用开销
是远远大于函数本身的
所以把这样的函数
定义为内联是非常合适的
你写了一个inline
那么每次需要GetOrigin、SetOrigin的时候
都自动地将这一段代码
展开在这个函数的调用处
而不调用这个函数了
代码量膨胀也不算厉害
因为函数体本身很短
同时整个程序的执行效率得到了提高
所以内联函数的用处、好处就体现在这个地方
不过它仅仅是和程序优化这个目的有关
和整个程序的功能
和整个程序的实现本身
它实际上是没关系的
所以如果真得没有内联函数
对我们程序来讲其实是没关系的
就是程序运行的效率可能会低一些
其它没有任何影响
第二个就是常数据成员
一个类定义的里边
实际上不仅仅可以带有变量
也可以带有常量
比如说这样一个例子 classA
在一个类的定义里边
定义了一个成员叫num
它是一个常整数 constint
用这个类来构造一个特定对象的时候
这个对象里面的num
在程序运行期间是不变的
这是非常重要的一个地方
常数据成员
这也就意味着这样一个量
不可以对它进行赋值
那么对它设定值的唯一一个机会
就在初始化的那一瞬间
所以一个类的所有的常数据成员
必须进行初始化
初始化写在什么地方呢
其实就是在它的初始化列表里边
还有一种情况
我在一个类里边定义了一个函数
它需要读取这个类中的数据
同时我们明确地知道这个成员函数
是不会修改成员的数据的
那么这样的函数在实现的语义来讲
我们有必要把它实现成一个常函数
这样的一个定义格式就意味着
这个函数必须加上一个const修饰
因为const是左结合的
所以你不能写在函数原型的前面
你只能写在函数原型的后边
在函数的原型写完了 写const
后面跟着一个分号 这样才可以
实现这个函数的时候
也要在函数的头部写上const
表示这个函数的实现是常成员函数
如果你在函数的实现的时候
没写const 或者你在函数原型的时候
没写const 两者不匹配
对于编译器来讲
它将认为这是两个不同的函数
它们的函数签名是不一样的
所以你要写就都写 要不写就都不写
这是一定要注意的一个地方
一旦一个函数不需要修改成员变量的值
或者不能修改成员变量的值
那么就应该把它定义成常函数
常的成员函数不能够调用
类的非常成员函数
这是个非常明确的要求
因为非常成员函数
它就有可能修改成员属性的值
所以你调用它
就有可能打破常成员函数的语义
编译器本身不允许你做这样的调用
静态的成员函数不能定义为常成员函数
静态函数我们还没讲 我们待会再讨论
如果这个对象是一个常量
那么因为它的值不能修改
那就意味着你只能在它的上面
调用常成员函数
它的非常成员函数你都没有权利访问的
这是不允许操作的
正是在这个角度上来讲
如果你定义了一个类的类型
并且你知道未来你会用这个类类型
定义一个常对象
那么这个类类型的接口里边
那些凡是不需要修改成员的属性值的
这样的函数 都应该定义成常函数
接下来就是静态数据成员
静态数据本身我们其实已经见过了
它的意思就是我们的静态数据只有一份
那么在类类型的定义里面
如果我有一个数据成员
也就是它的一个属性 只包括一个
就是整个这个类——不管你通过这个类
定义了多少个对象
这些对象都共享同一个数据成员
这个数据成员就唯一一份
这个类的所有对象都共享它
那么在这种情况下边
你就应该将这个成员
定义为静态数据成员 普通的数据成员
每一个对象有自己的独立的一份
你用这个类类型定义一个对象
它就构造了一份它的全部数据成员
再定义了一个对象 又构造一份
这个两者是不一样的 它是两个物体
两个对象 两个数据
如果你要求一个特定的数据成员
在这个类中所有的对象上面都一样 就一份
那么你就应该使用静态数据成员
因为是所有的类都共享同一个对象
所以静态的数据成员
不会在这个类的对象上面分配存储空间
它是单独分配的
但是访问规则仍然是属于这个类的
你要通过类名去解析
这个静态的数据成员才可以
静态数据成员必须在
我们这个类的外部进行初始化
初始化这个动作
和它的访问规则本身是无关的
意思就是说
你不管是将这个静态的成员声明为public
还是private 还是protected
都应该在这个类的外部
单独地对它进行初始化
而不能在类的内部对它进行初始化
这是特别需要注意的一个地方
这个静态数据成员初始化的动作
应该放在源文件 而不是头文件里
因为它相当于一个变量的定义一样
除了静态数据成员 还有静态的成员函数
我们定义静态成员函数的目的
仍然是在一个类
而不是在一个对象上面对它进行调用
它就是为了访问类的静态的成员而设置的
定义一个静态数据成员
你要想访问这个类的静态数据成员
那么使用静态的成员函数
是一个非常恰当的方式
如果你要访问这个类的非静态的数据成员
那么你必须提供这个非静态的数据成员
到底是属于哪一个对象的
也就是说 你必须提供一个对象
或者一个对象的指针
或者一个对象的引用
这样的话 它才知道你提供的这个东西
到底是哪一个对象的
这是非常重要的一个地方
我们看这样的一个例子
class A,public
我定义一个静态成员函数staticintf()
定义一个静态函数staticintg
里面有一个静态的数据对象staticintcount
静态数据成员在外部对它进行初始化
这是“.cpp”代码 那是“.h”代码
然后静态的成员函数
我就按照像这样的一个方式
去访问它就行了
因为那个数据是类里独一份
所以我们这样的一个函数
理论上来讲 直接用它的名字
就访问到了那独一份的数据成员
如果你在这个静态成员函数里边
想访问它的非静态数据
你可以做 但是你必须告诉它
你到底访问的是哪一个对象
我们这里面传了一个constA的一个引用
A::g 访问的就是a引用的那个对象
所以returna.num
这是一个非常明确的要求
为什么呢 就是我们的静态的成员函数
它并没有缺省的this指针
-1.1 提纲
-1.2 程序设计的基本概念
-1.3 简单C/C++程序介绍
-1.4 程序设计的基本流程
-1.5 基本语法元素
-1.6 程序设计风格
-1.7 编程实践
-第一讲 C/C++基本语法元素--编程实践提交入口
-2.1 提纲
-2.2 结构化程序设计基础
-2.3 布尔数据
-2.4 分支结构
-2.5 break语句
-2.6 循环结构
-2.7 编程实践
-第二讲 程序控制结构--编程实践提交入口
-3.1 提纲
-3.2 函数声明、调用与定义
-3.3 函数调用栈框架
-3.4 编程实践
-第三讲 函数--编程实践提交入口
-4.1 提纲
-4.2 算法概念与特征
-4.3 算法描述
-4.4 算法设计与实现
-4.5 递归算法(一)
-4.6 递归算法(二)
-4.7 容错与计算复杂度
-4.8 编程实践
-第四讲 算法--编程实践提交入口
-5.1 提纲
-5.2 库与接口
-5.3 随机数库(一)
-5.4 随机数库(二)
-5.5 作用域与生存期
-5.6 典型软件开发流程(一)
-5.7 典型软件开发流程(二)
-5.8 编程实践
-第五讲 程序组织与开发方法--编程实践提交入口
-6.1 提纲
-6.2 字符
-6.3 数组(一)
-6.4 数组(二)
-6.5 结构体
-6.6 编程实践
-第六讲 复合数据类型--编程实践提交入口
-7.1 提纲
-7.2 指针基本概念
-7.3 指针与函数
-7.4 指针与复合数据类型(一)
-7.5 指针与复合数据类型(二)
-7.6 字符串
-7.7 动态存储管理(一)
-7.8 动态存储管理(二)
-7.9 引用
-7.10 编程实践
-第七讲 指针与引用--编程实践提交入口
-8.1 提纲
-8.2 数据抽象(一)
-8.3 数据抽象(二)
-8.4 链表(一)
-8.5 链表(二)
-8.6 链表(三)
-8.7 链表(四)
-8.8 函数指针(一)
-8.9 函数指针(二)
-8.10 抽象链表(一)
-8.11 抽象链表(二)
-8.12 编程实践
-第八讲 链表与程序抽象--编程实践提交入口
-9.1 提纲
-9.2 程序抽象与面向对象
-9.3 类类型
-9.4 对象(一)
-9.5 对象(二)
-9.6 类与对象的成员(一)
-9.7 类与对象的成员(二)
-9.8 类与对象的成员(三)
-9.9 继承(一)
-9.10 继承(二)
-9.11 继承(三)
-9.12 多态(一)
-9.13 多态(二)
-9.14 编程实践
-第九讲 类与对象--编程实践提交入口
-10.1 提纲
-10.2 四则运算符重载(一)
-10.3 四则运算符重载(二)
-10.4 关系与下标操作符重载
-10.5 赋值操作符重载(一)
-10.6 赋值操作符重载(二)
-10.7 赋值操作符重载(三)
-10.8 赋值操作符重载(四)
-10.9 赋值操作符重载(五)
-10.10 流操作符重载(一)
-10.11 流操作符重载(二)
-10.12 流操作符重载(三)
-10.13 操作符重载总结
-10.14 编程实践
-第十讲 操作符重载--编程实践提交入口
-11.1 提纲
-11.2 泛型编程概览
-11.3 异常处理机制(一)
-11.4 异常处理机制(二)
-11.5 运行期型式信息(一)
-11.6 运行期型式信息(二)
-11.7 模板与型式参数化
-11.8 题外话:术语翻译
-11.9 泛型编程实践(一)
-11.10 泛型编程实践(二)
-11.11 泛型编程实践(三)
-11.12 泛型编程实践(四)
-11.13 泛型编程实践(五)
-11.14 泛型编程实践(六)
-11.15 泛型编程实践(七)
-11.16 泛型编程实践(八)
-11.17 泛型编程实践(九)
-11.18 泛型编程实践(十)
-11.19 编程实践
-第十一讲 泛型编程--编程实践提交入口
-12.1 提纲
-12.2 程序执行环境(一)
-12.3 程序执行环境(二)
-12.4 程序执行环境(三)
-12.5 程序执行环境(四)
-12.6 输入输出(一)
-12.7 输入输出(二)
-12.8 文件系统
-12.9 设备
-12.10 库(一)
-12.11 库(二)
-12.12 makefile文件(一)
-12.13 makefile文件(二)
-12.14 makefile文件(三)
-12.15 编程实践
-第十二讲 Linux系统编程基础--编程实践提交入口
-13.01 提纲
-13.02 进程基本概念
-13.03 信号
-13.04 进程管理(一)
-13.05 进程管理(二)
-13.06 进程管理(三)
-13.07 进程间通信(一)
-13.08 进程间通信(二)
-13.09 进程间通信(三)
-13.10 进程间通信(四)
-13.11 进程池
-13.12 编程实践
-第十三讲 进程编程--编程实践提交入口
-14.1 提纲
-14.2 线程基本概念
-14.3 线程管理(一)
-14.4 线程管理(二)
-14.5 线程管理(三)
-14.6 线程管理(四)
-14.7 线程同步机制(一)
-14.8 线程同步机制(二)
-14.9 C++11线程库(一)
-14.10 C++11线程库(二)
-14.11 C++11线程库(三)
-14.12 C++11线程库(四)
-14.13 C++11线程库(五)
-14.14 编程实践
-第十四讲 线程编程--编程实践提交入口
-15.1 提纲
-15.2 Internet网络协议
-15.3 套接字(一)
-15.4 套接字(二)
-15.5 编程实践
-第十五讲 网络编程--编程实践提交入口