数据抽象和类 Ⅰ
基本概念
抽象
Data Abstraction:只关心该数据“是什么”“如何使用”,而不关心其如何运作。
Control Abstraction:只关心该行为能实现什么,而不关心其具体实现方法。
抽象数据类型
在程序中,称被抽象的数据,为抽象数据类型(Abstract Data Type, ADT)
一种ADT应具有:
- 说明部分:描述数据值的特征和作用于这些数据之上的操作,用户仅需明白其说明,无需知晓内部实现。
- 实现部分。
抽象数据类型转化
把DATE
设计为一种数据类型。
- 内部包含年月日等数据以及在这些数据上可进行的操作。
- 用户利用
DATE
就可以定义多个变量。 - 用户可调用每个变量中公开的操作,但无法直接访问每个变量中被隐藏的内部数据。
- 用户也无需关心变量中各操作的具体实现。
- 于是
DATE
就是一种封装好的数据类型。这就达到了信息隐藏和封装的目的。
结构体 类 对象
C
中的结构体
C
数组允许定义可存储相同类型数据项的变量,结构是 C
编程中另一种用户自定义的可用的数据类型,它允许您存储不同类型的数据项。
结构用于表示一条记录,我们继续以上面的日期为例,我们可能会关心:
- Year
- Month
- Day
但C语言中的 struct
只能包含变量,
不能包括实现ADT当中操作的函数
1 | struct Date { |
C++
对象&类
新增面向对象编程
1 | class classname { |
类的示例
1 | class Date { |
在{}
中列出类的成员。类的成员包括:
- 数据成员:一般说来,数据成员是需要隐藏的对象;即外部的程序是不能直接访问这些数据的,应该通过函数成员来访问这些数据。所以一般情况下,数据成员通过关键字
private
声明为私有成员(private member)。 - 函数成员:通过关键字
public
声明为公有成员(public member)。外部程序可以访问共有成员,但无法访问私有成员。 - 对于类的使用者(用户代码),只需要获得
DATE.h
,即可调用类对象的公有函数访问其内部的数据成员。使用者无法直接访问私有成员,无需知晓公有函数的内部实现。
结构体 v.s. 类
C
的 struct
只能包含变量,而 C++
的 class
还可以包含函数。set()
是用来处理成员变量的函数,在 C
中,我们将它放在了 struct Date
外部,和成员变量是分离的;而在 C++
中,我们将它放在了 class Date
内部,使它和成员变量聚集在一起,看起来更像一个整体。
对象
通过结构体定义出来的变量传统上叫变量,而通过类定义出来的变量有了新的名称,叫做对象(Object)。
1 | Date date; |
成员函数
类的成员函数指,把定义和原型写在类定义内部的函数,就像类定义中的变量一样。
类成员函数是类的一个成员,它可以操作类的任意对象,可以访问对象中的所有成员。
成员函数可以定义在类定义内部,或者单独使用范围解析运算符 ::
来定义。在类定义中定义的成员函数把函数声明为内联的,即便没有使用 inline 标识符。
1 | class Date { |
1 | class Date { |
预处理命令
在 DATE.cpp 文件开头需要加入预处理命令
#include "DATE.hpp"
这是因为在 DATE.cpp 中要用到用户自定义的标识符 DATE
,而它的定义在 DATE.hpp 中。
在 DATE.hpp 中,各函数原型是在 {}
中的。根据标识符的作用域规则,它们的作用范围仅在类定义中,而不包括 DATE.cpp 。因此在 DATE.cpp 中需要利用作用域解释运算符 ::
来指明这里的函数是类 DATE
里的成员函数。
DATE.cpp 中有时还包括 DATE
内部要使用到的函数,例如daysInMonth。
这种函数并非对外公开供用户使用,因此可以将其声明为私有成员。
若在该函数中没有涉及该类的数据成员,则无需将它们声明为类的成员。
调用成员函数和成员变量是在对象上使用点运算符(.),这样它就能操作与该对象相关的数据
1 | date.flag = 1; |
类的静态成员
静态(static)成员是类的组成部分但不是任何对象的组成部分
- 通过在成员声明前加上保留字
static
将成员设为static(- 在数据成员的类型前加保留字
static
声明静态数据成员; - 在成员函数的返回类型前加保留字
static
声明静态成员函数)
- 在数据成员的类型前加保留字
- static成员遵循正常的公有/私有访问规则。
C++
程序中,如果访问控制允许的话,可在类作用域外直接(不通过对象)访问静态成员(需加上类名和::
)- 静态数据成员具有静态生存期,是类的所有对象共享的存储空间,是整个类的所有对象的属性,而不是某个对象的属性。
- 与非静态数据成员不同,静态数据成员不是通过构造函数进行初始化,而是必须在类定义体的外部再定义一次,且恰好一次,通常是在类的实现文件中再声明一次,而且此时不能再用
static
修饰。 - 静态成员函数不属于任何对象
- 静态成员函数没有
this
指针 - 静态成员函数不能直接访问类的非静态数据成员,只能直接访问类的静态数据成员
1
2
3
4
5
6
7
8
9
10
11//date.hpp
class Date {
private:
static int count;
...
public:
static void getCount();
...
};1
2
3
4
5
6
7
8//date.cpp
int Date::count = 0;
//必须在类外定义体的外部再定义一次
void Date::getCount() {
return count;
}