1. OOP 三特性

封装 :将相关数据和操作数据的方法打包成一个类。不同的类相互隔离,也可以自由组合。
继承 :从一个父类衍生出子类,子类可以自然地拥有与父类的相同的属性和行为。
多态 :子类与父类或者兄弟类在某一种行为上有所区别,即同一函数不同实现。
个人理解,继承保持了类之间的共性,多态使得这些具有共性的类之间有各自的特性。

2. 封装

类是一组数据以及操这组数据的函数(方法)的集合。类是对象的抽象模板,对象是类的具体实例,给类的数据取不同的值,同一个类就产成了不同的对象。

数据

于是数据应该有两种:一种是与 类级别 的,同一个类取值都一样,与实例无关;另一种是 实例级别 的,同一个类的不同实例取值各不相同。

数据类别 C++ Python
类级别 静态数据成员 类变量
实例级别 非静态数据成员 实例变量

方法

既然数据有两种,方法至少也应该有两种,一种是类级别的,一种是实例级别的。类级别的数据在实例化之前就存在,在实例化之前操作类级别的数据,是一种方法。实例化之后产生了实例级别的数据,这时候的方法可以同时操作两类数据,是另一种方法。

可操作数据 C++ Python
类级别 静态成员函数 类方法
类级别和实例级别 非静态成员函数 实例方法

C++ 中还有一种重要方法是 虚函数,使用虚函数可以实现 C++ 中的多态。
Python 中还有一种方法是静态方法。在 Python 中可以认为,实例方法传入了实例对象的指针,类方法传入了类的指针,而静态方法既不需要传入实例,也不需要传入类。

3. 继承

子类继承父类,使子类拥有父类的数据和方法。

单一继承

这种情况下,python 和 C++ 的最大区别应该在于继承方式。C++ 继承分为 public、private、protected 三种,Python 都是 public。

多重继承

没有虚函数的情况下,区别主要有两点:
(1) 假设函数名为 fun,当多个父类中定义方法 fun,而子类没有定义方法 fun,通过子类调用方法 fun,C++ 会 报错 ,Python 会使用MRO 来确定调用哪个父类的 fun。
(2) 对菱形继承的情况,C++ 要使用 虚继承,Python 要使用super 结合 MRO

4. 多态

多态在代码上的表现为一个方法多个实现。C++ 的多态必须建立在继承基础上,现有继承,后有多态。Python 的多态没有继承关系的限制,只要实现了同名方法即可。

C++ 多态

前文介绍了 C++ 对象的内存模型,这里只说最简单的单一继承情况。C++ 通过父类的指针或引用调用虚函数,在编译期间无法确定调用的是父类的实现还是子类的实现,只有在执行期间访问内存模型中的虚函数表才能确定。
假设 Derived 类继承 Base 类,Base 类中定义了虚函数 method,Derived 类重写了虚函数 method,此时 Base 类和 Derived 类的对象模型如图:

执行如下代码:

1
2
Base *ptr = new Derived();
ptr->method();

编译器看到 ptr 是 Base 类型,如果 method 不是虚函数,那么执行的应该是 Base::method。现在 method 是虚函数,执行期调用 Base::method 还是 Derived::method,要看赋给 ptr 的是 Base 对象地址还是 Derived 对象地址。上面的代码是创建了一个 Derived 对象,并把地址传给 Base 类指针,但是内存模型中的 vptr 指向的仍然是 Derived 类实现的虚函数,所以最后调用的是 Derived::method。

Python 多态

相比 C++ 复杂的内存模型,Python 的鸭子类型让多态更灵活(Python 内存模型跟多态的关系好像不大)。

1
2
3
4
5
6
7
8
9
10
11
12
class Cat:
@classmethod
def say(cls):
print("miao miao")

class Dog:
@classmethod
def say(cls):
print("wang wang")

def func(kls):
kls.say()

这里 Cat 和 Dog 没有继承关系,say 也不是虚函数,调用 func(Cat)和 func(Dog)都能正确执行。要是再定义个 Person 类,只要定义了方法 say,就可以把 Person 传给 func 完成调用。

5. 写在最后

虽然都是从 C 语言发展出来的 OOP 语言,C++ 和 Python 的区别还是挺大的,特别是多态的处理,所以对相同逻辑的多态执行结果也是有区别的:

C++ 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

class Base
{
public:
int x = 1;
virtual void print() { std::cout << x << std::endl; }
};

class Derived : public Base
{
public:
int x = 2;
};

int main() {
Base *ptr = nullptr;
ptr = new Base();
ptr->print(); // 打印 1
ptr = new Derived();
ptr->print(); // 打印 1
}

Python 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Base:
def __init__(self):
self.x = 1
def print(self):
print(self.x)

class Derived(Base):
def __init__(self):
self.x = 2

def func(Kls):
obj = Kls()
obj.print()

func(Base) // 打印1
func(Derived) // 打印2