背景
面向对象编程
首先,面向对象编程(Object-Oriented Programming, OOP)是一种编程风格/程序设计思想/编程范式。它强调以对象(数据+方法)为中心,而不是以过程为中心(即面向过程编程)。
由此可见,从某种意义上讲,编程语言可以在不同程度上支持这种风格。即使 C 语言也可以实现这种风格,只不过实现起来难度更大、更为曲折。
C++ 就是对 OOP 特性支持的比较好的编程语言。
抽象
抽象(abstraction)是一种 OOP 特性。处理复杂问题的方法之一就是提供简单的抽象。那么,抽象究竟表示什么意思?
这里的抽象表示去除无关信息或隐藏细节信息,以减少问题复杂度的过程。
一个典型的例子是:对于老司机开车这件事,我们只需要关心速度、油量等指标(数据表示);以及启动、换挡、踩油门、刹车、转方向盘、开闪光灯等操作过程(数据操作)。我们不需要知道发动机究竟怎么启动的,电路和管路是怎么布局的等问题。
因此,这里使用抽象的一个关键就是:尝试从用户的角度去考虑对象的构建,先去定义用户和对象的交互(即接口),再去想对象的内部数据和接口实现。
封装
封装(encapsulation)是一种 OOP 特性。封装有多种含义,OOP 中的封装的语义是:将数据表示和数据操作,绑定到一个对象中,不能直接访问对象的数据,只能通过方法来访问或修改对象的数据。
类
C++ 中的类(class)就是一种提供抽象的方式,它也相当于自定义类型。
定义类需要确定两个部分:一种称为类声明;一种称为类方法定义。
类声明用于声明数据成员和成员函数(类的蓝图,常常放在头文件),类方法定义用于定义成员函数(类的细节,常常放在库文件)。
类声明部分
C++ 提供了访问控制关键字:public 和 private。这两个关键字可以限定对象中数据和方法的访问权限,private 提供了数据隐藏,进而加强了类的封装特性。
由于这只是声明,所以一般情况下,普通的成员函数只需要提供函数头。但是也有例外,有些成员函数可以直接提供函数定义,这种成员函数会隐式地定义为内联函数,又叫内联方法。内联函数一般用于短小的函数,编译器会进行优化,避免函数调用的栈开销,例如下面示例中的 SetGrade。
示例如下:
1 // student.h 2 #pragma once 3 #include4 #include <string> 5 6 class Student 7 { 8 public: 9 void InitializeData(const std::string& name, int score); 10 void SetGrade(int val) { grade = val; }; 11 void ShowGrade(); 12 13 private: 14 int grade; 15 std::string name; 16 };
这 Student 类的声明,放在头文件。它声明了类的数据成员和成员函数,但是没有给出普通成员函数(即非内联的)的定义。
从类的设计上来看,声明部分把数据成员放在了私有部分,而把成员函数放在了公有部分。公有部分构成了公共接口,而私有部分提供了数据隐藏。
类方法定义部分
在类方法定义部分,使用 :: 来为成员函数指定类名,它的术语叫作用域解析运算符(scope-resolution operator)。
类方法定义部分提供了实现细节。
1 // student.cpp 2 #include "student.h" 3 4 void Student::InitializeData(const std::string& name_val, int grade_val) 5 { 6 name = name_val; 7 grade = grade_val; 8 } 9 10 void Student::ShowGrade() 11 { 12 using namespace std; 13 cout << name << " has grade of " << grade << "." << endl; 14 }
我们看到,成员函数可以访问私有数据成员(name 和 grade)。
类的简单使用
student.h 和 student.cpp 构成了 Student 类的完整定义,接下来是 Student 类的简单使用。
1 // main.cpp 2 #include "student.h" 3 4 int main() 5 { 6 Student s1; 7 Student s2; 8 s1.InitializeData("zhangsan", 80); 9 s2.InitializeData("lisi", 59); 10 s1.ShowGrade(); 11 s2.ShowGrade(); 12 s1.SetGrade(99); 13 s1.ShowGrade(); 14 s2.ShowGrade(); 15 }
输出:
zhangsan has grade of 80. lisi has grade of 59. zhangsan has grade of 99. lisi has grade of 59.
这里的要点是,对象 s1 和 s2 都是基于同一个 Student 类创建的,遵循 Student 类提供的相同的抽象。但是对象 s1 和 s2 有自己单独的存储空间,互相不受影响。
但在 C++ 编译器的实现中,不同对象的成员函数/函数实际上是共享的,只有一个副本。但它显然可以找到调用对象的数据成员,因此可以看起来每个对象拥有一个自己的成员函数。
这可以提升编译效率,而不会影响 OOP 特性,即数据表示和数据操作都封装到一个对象里面。
下一篇:C++系列:对象和类(二)
参考
-
Object-oriented programming
-
Object-Oriented Programming In C++
- Abstraction in C++
-
《C++ Primer Plus Sixth Edition》 by Stephen Prata