当前课程知识点:基于Linux的C++ > 第九讲 类与对象 > 9.3 类类型 > LinuxCPP0903
我们刚才讲
按照刚才那个方式来实现struct POINT
那个抽象的点库
说它不是一个真正的C++代码
并不意味着它不能编译
它实际上是可以编译的 而且也能够运行
但是它不符合数据封装与信息隐藏的需要
也就是说 你不能够禁止程序员
直接操纵x和y
这是非常重要的一个地方
我们需要的就是能够对程序的访问和控制
加以限制的特殊数据结构
这样的特殊的数据结构我们就称它为类类型
我们这一节要讨论类类型
包括三个部分 一个是类的声明与定义
然后通过两个例子
来演示怎么定义一个类
然后是关于类类型
它的声明与定义的一些简单说明
我们首先来看类的声明与定义
类的声明 当你仅仅声明类的存在
而不提供类的细节的时候
那么你就使用关键字class
来声明一个类就可以 “classA;”
完成了一个类的声明
这种声明我们俗称前置声明
因为它没有定义这个类的细节
如果你需要定义这个类的细节
那就是类的定义
那么它的一般定义格式就是:
classA 后面花括号对
里面跟着它的数据成员和成员函数的定义
你一一地书写在它的类的
花括号体里面就可以了
这里比较特殊的地方
就多了“public:”、“protected:”、“private:”
这样的控制规则 我们称它为访问控制
你把这个访问控制规则去掉
把这几个关键字去掉
你就会发现这个类的定义
和我们结构体的定义就没差别
这是非常重要的一点
实际上 在C++代码里面
使用struct一样可以定义类
它们唯一的差别就体现在它的访问控制上
尤其是它的缺省的访问控制上面
我们来看这三个关键字
public 它表示公开的访问控制
也就是说 在“public:”后面定义的
所有的成员——不管是数据成员还是成员函数
它都是向外界公开的
谁都可以看到 谁都可以访问
protected 它后面定义的数据成员或成员函数
有限地公开
在自己这个结构体或这个类的内部
都是可以自由访问的
但是外界想访问它是有一个限制条件的
只有这个类的派生类才能访问它
“private”表示后面定义的
所有的数据成员和成员函数
都是完全私有的 也就是本对象的隐私
只有本对象的内部才可以访问
外界是没有权利查看的
也没有权利修改的
public、protected、private这三个保留字
定义的顺序
谁放在前面、谁放在后面都可以
而且每一个关键字都可以出现很多次
没关系 你先public 后private
然后是protected 可以
你先public 后protected 后private 可以
你先private 后public 也可以
没有protected 也行
定义本身是随意的
如果某些数据成员或成员函数
前面没有访问控制
没有的意思不是说在这一行的上面没有
每一个像这样关键字的定义
在出现它的下一个关键字之前
它的作用的数据成员和成员函数
是向后自动地延拓的
也就是说 每一个public、protected、private
这样的关键字的后边
它这里面所包含的数据成员或成员函数
可是有很多个的 这是没有关系的
我们说的 “没有定义访问控制的那些数据成员
和成员函数”是指在它前面
实际上没有书写任何一个
这样的访问控制关键字
你比如说第一行“public:”给取消掉
那么这个“成员类型成员名称”
第一个定义的成员名称 它的一个成员
它前面实际上就没有访问控制
C++语言的规定呢
我们的class定义的类类型里边
没有访问控制的数据成员和成员函数
缺省是私有的 也就是缺省是private的
如果你使用struct那个关键字来定义类类型
这在C++代码中是允许的
那么缺省的这样的数据成员和成员函数
就是public 就是公开的
这是struct和class最重要的区别
也就是说 在C++代码里面
你想定义类类型的时候
用struct还是用class其实都是可以的
差别就体现在它的访问控制上
尤其是当你没有书写访问控制的时候
struct 它的成员是公开的
而class 它的成员就是私有的
在定义上就这一点差别
我们看这个点类库的接口
有了类类型这个概念
我们很容易就书写
下面这样一个类库:一个“class Point{};”
在public关键字里面提供一系列的成员函数
Point、~Point 然后GetValue、SetValue、Compare、
TransformIntoString 然后是Print
接下来就是private关键字
后面跟着“intx, y” 就是数据成员的定义
我们先来看public 我们定义了什么呢
定义了一系列的公开的操作
在public这个访问控制下面
是没有数据成员的
我们仅定义了一系列的点类库的操作
也就是说 我们只公开这个点类库的操作
而不公开它的实现细节
里面有两个很特殊的函数:
一个是和类的名字
完全一模一样的函数 叫Point
还有一个名字跟类名一样Point
但是前面多了一个“~”
这样的两个函数非常特殊
第一个是构造函数 第二个是析构函数
我们后面再解释 现在我们当作它不存在
来理解剩下的几个函数:
GetValue、SetValue、Compare等等
这些函数和我们第一版的
那个抽象的点库啊
它不一样就是什么呢
函数名字前面缺少“Pt”起头的那两个字符
就是说 因为我们已经完成了
数据封装和信息隐藏
那么这些函数本身就定义在这个类的
接口的内部
我们完成了这样的一个类库中的成员函数
和数据的有效地统一
第二个方面呢
就是我们将它的成员x和y
完全地定义为私有的
外界是不可以访问的
我即使把它写在这个点库里面
放在“point.h”这个头文件里
一旦包含了这个“.h” 这个头文件
从理论上来讲 它就能够看到
这个class定义里面全部的细节
它实际上是能够看到这两个成员x和y的
但是因为访问规则的限制
导致在外界 也就是这个classPoint
这个类的外界
你即使能看到x和y这两个数据成员
你也没有权利去访问它
我们通过这一点
达到了数据封装与信息隐藏的目的
同时 因为我们将这些数据成员和
数据成员上面所应该具有的操作集
统一地封装在这个classPoint的这个类的内部
从而完美地实现了属性和行为的统一
这是非常非常重要的一个实现细节
为什么我们要引入面向对象
就是这个原因
接下来我们来看第二个例子
就是实现一个圆的类库的接口
我们要设计表示二维平面上圆的类类型
我们定义一个类classCircle
里面一样地会有一系列的成员函数
和它的私有的数据字段
这些成员函数呢是公开的
我们有GetOrigin、SetOrigin、GetRadius、
SetRadius、GetPerimeter和GetArea
我们有这些函数
还有私有的三个字段r、x、y
从实现的这个角度来讲
和我们刚才那个例子是有点差别的
实际上我们没有定义
Circle这个类同名的那个函数
和带“~”的那个函数
也就是说我们没有定义这个类的
构造函数和析构函数
这在C++代码里面也是允许的
你如果没定义 编译器就会自动给你
生成一个缺省的构造函数
这个详细的细节我们待会再解释
从接口这个角度来看
我们这个圆类库和刚才那个点类库
其实是一样的
它们都完成了抽象的类库的表达
接下来我们来看圆类库的实现
我们会实现这里面一系列的函数
比如说Circle里面的成员函数叫GetOrigin
这样实现的函数
因为它从属于这个类的嘛
所以它就不是普通的函数
那么我们在实现这个函数的时候
那我们怎么表达这个函数
是从属于一个特定类的信息呢
那么在C++编译器里面
你必须按照这样的规则来写:
这个函数的名字前边要加上这个类的名字
后面跟着“::”
这是一个解析操作符 类名加上“::”
我们解析这个GetOrigin
表示这个GetOrigin这个函数
是从属于Circle这个类类型的
所以它才被称为成员函数嘛
就是这个道理
每一次在定义这样函数的时候
都要按照像这样的一个方式去解析它
在函数名字前面加上“类名::”
但是你注意看 不管是GetOrigin还是SetOrigin
我们想设置这个圆的原点
或者获取这个圆的原点信息的时候
传的两个参数的名字都叫x和y
它刚好和我们这个圆类型本身内部定义的
那个坐标名字是相同的
那么你在这个代码里面就很难写
这个x到底是代表着这个形式参数的x呢
还是代表着这个类类型里面的
那个私有的数据对象的那个x呢
这是个非常重要的问题
也就是说 名字本身产生了冲突
因为我们说过了
在我们类类型里面定义的所有的数据成员
那么在类的所有的函数里面
也就是类的成员函数里面
都是可以自由访问的
那么在这里面到底是把形式参数的x
赋值给类的数据成员的x
还是把数据成员的x
赋值给我们的形式参数的x
当然我们这里面是指针所以是“*x”
到底是哪一个呢 这个就可能导致问题
为了区分我们的参数
和我们的类的数据成员
所以C++给我们的对象提供了一个this指针
this这样的一个关键字
它指代这个对象本身
这句话可能不太好理解
这个对象在哪里呢
我现在仅仅是在定义
我们这个类的成员函数啊
你这么想 当我这个程序在运行的时候
这个成员函数将会由某一个对象来发起
你注意这个操作是主动性的
这个函数必须是在一个对象上面调用
发起调用的那个对象
当然是属于我们这个Circle类的
它是谁呢 我们就用this指针指向它
this指针 也就是说就代替了那个对象自身
我们就用this指针指向那个对象
有了this指针 我们就可以在this指针上面
引领它的所有成员
引领它的x成员 引领它的y成员
当然你也可以引领它的成员函数
引领比如说GetOrigin、SetOrigin
这样的成员函数 都可以
我们刚好用它来区分形式参数的x
和类的成员的那个x
这是非常重要的一个技术
同学们一定要了解
其它几个函数的实现
GetRadius、SetRadius、GetPerimeter、GetArea
这些函数都非常简单 我就不解释了
类类型的声明有一些特别需要说明的地方
一个 仅限于函数原型使用类类型的声明
如果我有一个类类型的前置声明
你比如说classB
你没有定义它 你仅仅是声明了它
后面跟着一个classA
A类的一个定义 那么在A类里面
A类的成员函数func
可以带B类的形式参数
这是没有问题的
你不能作为函数定义的参数
因为真正到函数定义
就必须使用那个类的对象了
所以那个仅有类类型的声明是不够的
但在函数原型里面
可以只有类类型的声明
而没有它的定义 这是没有问题的
但是你也不能够在这个类的定义里面
用前置声明的那个B类
来定义这个数据成员
这一点是和函数实现里面
那个形式参数一样
你是不能够定义它的数据对象的
因为你要定义这个数据对象
就涉及到了对这个数据对象进行内存分配
而内存分配的模式实际上我们是不知道的
只有类的声明是不知道的 必须要有定义
所以类类型的声明
是不能够用于定义类的数据成员的
-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 编程实践
-第十五讲 网络编程--编程实践提交入口