tags:
- Cpp
Object Oriented Programming in C++
在课堂中,我们学过 OOP 的三大特征,即封装、继承和多态。但时过境迁,OOP 的编程范式随着各种新技术的出现而不断更新。在这个文档中,我们会概述现代的 OOP 编程(A best practice)。
Separation from interface and implementation.
我们先从最基本的封装性谈起。C++ 用 “类类型 (class types)” 来对数据进行封装。封装很好理解,即将类内成员变量和函数与外界分开。不像在 C 语言中,函数的实现光溜溜地暴露在全局范围 (global scope) 。在 C++ 中,我们可以用类类型将这些函数的实现封装起来,只暴露使用的接口。
C++提供三种类类型:class
、struct
、,而只有前两者提供对数据的有效封装性,我们忽略union
union
类型。
Don’t inherit for code reuse. Inherit, when you want to express a logical structure.
继承是对一个类的继承。当我们继承一个类得到新的类时,我们需要明白,在不同的继承访问控制修饰符下,成员变量的可见性和访问权限是怎么样的。
class Account{
public:
int pub{0};
protected:
int prot{0};
private:
int pri{0};
};
class PubAccount: public Account{
public:
PubAccount(){
pub + prot; // public + protected
}
};
class ProtAccount: protected Account{
public:
ProtAccount(){
pub + prot; // protected + protected
}
};
class PriAccount: private Account{
public:
PriAccount(){
pub + prot; // private + private
}
};
int main(){
PubAccount pubAccount;
ProtAccount proAccount;
PriAccount priAccount;
pubAccount.pub;
}
为了封装性,基类的私有成员是不可以被继承到派生类中的。上面的例子中,展示了基类的不同成员的访问权限在不同继承方式下的访问权限。
class
and struct
在C++中,class
和 struct
的区别就是 struct
因为C兼容的缘故,默认的成员变量和成员函数是 public
的,而不像 class
中的 private
。还有一个区别就是继承时,struct
默认的继承方式为 public
,而 class
默认的继承方式是 private
。这就是它们的区别。
Make every class in your hierarchy either a base-only or leaf only.
The separation of the interface and its implementation is one of the crucial ideas of modern software design.
多-态,即一种物体的多种状态。多态分为编译时多态性和运行时多态性。在OOP中,通常指后者。
也称为静态多态性,主要通过函数的重载和模板实现。C语言并不支持相同函数名的重载,C++通过将参数类型也作为符号名的一部分来支持函数的重载。如plus(int i)
的符号名可能是_Z4plusi
,最后面的i
就表示有一个int
类型的参数。如果在类中可能是这样的_ZN5Class4plus
由于这种多态性在编译期就确定下来了,所以效率要高一点。由于这种在编译时就确定的多态性,有时并不将静态多态性看作是真正意义上的多态。我们用模板的代码简单演示一下。
#include <iostream>
template<typename T>
T plus(T x){
return x+1;
}
int main(){
auto y = plus(20);
auto z = plus(3.14);
auto e = plus('c');
return 0;
}
由于模板只有在调用相应类型的时候才会实例化,所以在这段代码中,我们在编写代码时就能说出来编译后会相应地产生三个关于 plus
的函数符号。而且这三个符号是独立的,链接时并不将其看作一个函数看待。
编译完成后查看符号表,我们确实看到了三个不同的函数符号:
du@DVM:~/Desktop/DSA$ nm -n stat_poly
U __cxa_atexit
U __dso_handle
U _GLOBAL_OFFSET_TABLE_
U _ZNSt8ios_base4InitC1Ev
U _ZNSt8ios_base4InitD1Ev
0000000000000000 T main
0000000000000000 W _Z4plusIcET_S0_
0000000000000000 W _Z4plusIdET_S0_
0000000000000000 W _Z4plusIiET_S0_
0000000000000000 b _ZStL8__ioinit
0000000000000047 t _Z41__static_initialization_and_destruction_0ii
000000000000009d t _GLOBAL__sub_I_main
也称动态多态性,通过虚函数和继承来实现。由于虚多态只有在程序运行时才能确定实际调用的时哪个函数,所以效率较低。(存储虚表造成的空间复杂度和虚指针索引导致的时间复杂度增加)
相关请参阅Virtual Dispatch in C++。
OOP is a programming paradigm in C++ using polymorphism based on runtime function dispatch using virtual functions.
在OOP范式中,由于派生类是基类派生而来的,所以它们的库所用的API是相同的。通过创建对象将派生类实例化,每个对象可以具有不同的状态和行为。这种特性就是由"virtual"所提供的动态多态性。即基类定义的API可以被派生类对象重写,从而在不同对象中表现出不同的实现。在编译期(compile time),确定派生对象的类型。在运行时(runtime),我们才能知道派生类的状态。