之前在面试的时候被问过懂不懂C++,懂不懂"多态"。我之前搞科研一直在用Python,不会C++。完全没听过"多态"这个词,只听说过"多模态"(无端联系,搞深度学习搞的)。全文主要记录了我的几大疑问:什么是多态?什么是面向对象编程?Python是面向对象编程(OOP)语言吗?什么是编译?除了面向对象编程,还有哪些编程范式?
一、什么是多态?
多态的字面意思是"多种形态"。在C++中,它指通过统一的接口调用不同类型的对象,产生不同的行为结果。例如:你可以命令"动物"发出叫声,但具体是猫叫还是狗叫,取决于实际对象的类型。
1. 如何实现多态?
最常见的多态形式是通过虚函数 和继承 实现的。
虚函数:在基类中用virtual
关键字声明函数,子类可以重写(override
)该函数。
cpp
class Animal {
public:
virtual void sound() { cout << "动物叫" << endl; } // 虚函数
};
class Cat : public Animal {
public:
void sound() override { cout << "喵喵" << endl; } // 重写虚函数
};
int main() {
Animal* animal = new Cat();
animal->sound(); // 输出"喵喵"而非"动物叫"
}
此时,基类指针animal
实际指向的是子类对象,调用虚函数时会自动选择子类的实现。
看到这里我隐约想到神经网络里面,Python搭建网络的时候继承父类nn.Module
。
2. 为什么需要多态?
- 代码复用性:通过基类接口统一处理所有子类对象,避免重复代码。
- 扩展性:新增子类时(如添加"鸟"类继承"动物"),原有代码无需修改,只需实现新的虚函数即可。
- 灵活性:程序运行时可以根据实际对象类型动态调整行为,例如游戏中的不同角色执行不同的攻击动作。
想象你有一个通用遥控器(基类指针),可以控制不同品牌的电视(子类对象)。按下"开机"按钮(调用虚函数)时,索尼电视会显示LOGO,三星电视会播放启动音乐------这就是多态:同一个操作(开机),不同对象(电视)产生不同行为。
通过掌握多态,我们可以写出更优雅、易维护的代码,这也是面向对象编程的核心优势之一。
二、什么是面向对象编程?
我在看C++科普帖子的时候,总是能看到说它是面向对象编程,我一直不理解这个概念。面向对象编程?那我要是单身,没有对象呢?面向谁啊?
1. 什么是面向对象编程(OOP)?
面向对象编程(OOP)是一种以对象 为核心的编程范式,它将程序中的数据和操作数据的逻辑封装成独立的"对象"。每个对象包含属性 (数据)和方法(操作),并通过类来定义同类对象的共同特征。例如,"狗"是一个类,而具体的某条狗(如"旺财")是一个对象,它有自己的颜色(属性)和吠叫(方法)。
2. 什么是对象?
对象是类的实例化结果,是程序中具体存在的事物。例如:
类:人类(包含"姓名""年龄"等属性,以及"说话""行走"等方法)。
- 对象:张三(姓名属性为"张三",年龄属性为20)。
- 对象的特点是将数据和行为统一管理,隐藏内部实现细节(封装性)。
那我好像懂了,有点像搭建一个神经网络类,通过实例化传进不同的参数,改变网络的层数,得到不同版本的网络。
3. 为什么需要面向对象编程?
- 代码复用性:通过继承,子类可以直接复用父类的属性和方法。例如"猫"和"狗"继承"动物"类后,无需重复定义"呼吸""进食"等基础行为。
- 扩展性:新增子类时(如"鸟类"继承"动物"),原有代码无需修改,只需重写虚函数或添加新方法。
- 维护性:封装保护了对象内部数据,避免外部随意修改(如通过
private
限制访问权限)。 - 灵活性:多态允许不同对象对同一操作做出不同响应。例如"动物"类的
sound()
方法,猫对象返回"喵喵",狗对象返回"汪汪"。
三、Python是面向对象编程(OOP)语言吗?
看了那么多,感觉有些地方跟Python很像,于是产生了疑问,Python是面向对象编程(OOP)语言吗?我记得大家都说它是脚本语言?脚本语言又是什么?
Python是一种完全支持面向对象编程的语言。它具备OOP的三大核心特性:封装、继承和多态。
- 封装 :Python通过类(
class
)将数据(属性)和操作数据的方法(函数)封装在一起,例如通过__init__
方法初始化对象属性,并通过访问修饰符(如双下划线__)控制属性的可见性。 - 继承 :子类可以继承父类的属性和方法,例如
class Cat(Animal)
表示Cat
继承自Animal
,子类可重写或扩展父类方法。 - 多态 :不同类的对象可以调用同名方法但表现出不同行为。例如,
Animal
类的sound()
方法在Dog
和Cat
子类中被分别实现为"汪汪"和"喵喵"。
1. 为什么说Python是脚本语言?什么是脚本?
脚本语言(Scripting Language)是以文本形式编写 、解释执行的编程语言,主要用于自动化任务和快速开发。其特点包括:
- 无需编译:直接通过解释器逐行执行,例如Python的.py文件由解释器即时解析。
- 动态类型:变量类型在运行时自动推断,无需显式声明。
- 胶水语言特性:常用于整合其他语言模块(如C/C++),简化复杂系统的构建。
2. Python如何同时支持OOP和脚本语言特性?
Python是多范式语言,既支持面向对象编程,又具备脚本语言的灵活性:
1) OOP的深度集成:
- 所有数据类型(如整数、字符串)本质都是对象。
- 通过类实现复杂逻辑封装,适合大型项目开发。
2) 脚本语言的便捷性:
- 单行代码即可完成简单任务(如
print("Hello World")
)。 - 交互式环境(REPL)支持即时测试代码片段。
四、什么是编译?
那我又产生疑问了?Python无需编译,那什么是编译?为什么有的语言需要编译,有的语言不需要编译?
1. 编译的定义与核心过程
编译是将高级编程语言 (如C、Java)编写的源代码转换为计算机可直接执行的机器码或中间代码(如字节码) 的过程。其核心流程包括以下阶段:
(1) 词法分析 :将源代码分解为有意义的单词(Token),例如识别变量名、运算符等。
(2) 语法分析 :根据编程语言的语法规则,构建抽象语法树(AST),检查代码结构是否符合规范(如括号是否匹配)。
(3) 语义分析与中间代码生成 :检查逻辑合法性(如变量类型是否匹配),并生成中间表示形式(如三地址码)。
(4) 代码优化 :对中间代码进行性能优化,例如删除冗余计算或简化循环结构。
(5) 目标代码生成 :将优化后的中间代码转换为特定平台的机器码或字节码(如Java的.class
文件)。
编译的最终产物是可执行文件 (如C语言的.exe)或平台无关的中间代码(如Java的字节码)。其优势在于执行效率高,但需要针对不同平台重新编译。
2. 为什么有的语言需要编译,有的不需要?
编程语言是否需要编译,取决于其设计目标 和执行方式。主要分为以下两类:
(1) 需要编译的语言(编译型语言)
典型代表 :C、C++、Rust。
原因:
- 性能优先:直接编译为机器码,无需运行时解析,执行速度快。
- 硬件级控制:适合开发操作系统、嵌入式系统等需要直接操作硬件的场景。
- 静态类型检查:编译时即可发现类型错误,提升代码安全性。
(2) 不需要编译的语言(解释型或混合型语言)
典型代表 :Python、JavaScript、Ruby。
原因:
- 解释执行:通过解释器逐行解析并执行代码,无需预编译,便于快速调试和跨平台运行。
- 动态特性支持:如Python的动态类型、反射等特性,依赖运行时环境解析代码。
- 混合执行模式 :
- 字节码+虚拟机(如Java):先编译为中间代码,再由虚拟机解释执行,兼具跨平台性和效率。
- 即时编译(JIT)(如JavaScript的V8引擎):运行时动态编译热点代码,提升执行速度。
(3) 编译与解释的核心区别
维度 | 编译型语言 | 解释型语言 |
---|---|---|
执行方式 | 先编译后执行(一次性转换) | 逐行解释执行(实时转换) |
运行效率 | 高(机器码直接运行) | 较低(需运行时解析) |
开发效率 | 较低(需反复编译) | 高(修改后直接运行) |
跨平台性 | 依赖平台(需重新编译) | 强(同一解释器支持多平台) |
错误检测 | 编译时发现语法/类型错误 | 运行时才能发现部分错误 |
五、除了面向对象编程,还有哪些编程范式?
我隐约记得,除了面向对象编程,还有个面向过程编程,也不懂是什么意思。
编程范式是解决问题的不同方法论。C语言是面向过程编程。面向过程编程的核心思想是以函数为基本单元,通过一系列步骤解决问题,关注"怎么做"而非"谁来做"。其他的编程范式以后再说。
面向过程与面向对象的对比:
维度 | 面向过程 | 面向对象 |
---|---|---|
基本单元 | 函数 | 类与对象 |
数据与逻辑 | 分离(数据通过参数传递) | 封装(数据与方法绑定在对象内) |
代码复用 | 低(需重复实现相似逻辑) | 高(通过继承和多态复用代码) |
适用规模 | 小型、简单任务(如算法实现) | 大型、复杂系统(如游戏引擎) |
六、C++基类和Python父类有什么区别?
1. 核心概念的相似性
C++中的基类(Base Class) 和Python中的父类(Parent Class) 在面向对象编程(OOP)中具有相同的核心目标:通过继承实现代码复用和多态性 。两者的基本定义和作用如下:
(1) 基类/父类的定义 :
• C++基类 :通过class BaseClass { ... };
定义,包含通用属性和方法,供派生类(子类)继承。
• Python父类 :通过class ParentClass: ...
定义,子类通过class Child(ParentClass): ...
语法继承其属性和方法。
(2) 核心功能 :
• 子类可以复用基类/父类的代码 (如方法、属性)。
• 子类可以覆盖基类/父类的方法 (C++通过虚函数virtual
,Python直接重写同名方法)。
• 支持多态性:通过基类指针/引用(C++)或动态类型(Python)实现不同子类对象的行为差异。
2. 实现差异与语言特性对比
尽管核心概念一致,但由于C++和Python的语言设计差异,基类与父类在具体实现上存在显著区别:
(1) 类型系统与继承机制
维度 | C++基类 | Python父类 |
---|---|---|
类型系统 | 静态类型,编译时检查继承关系和类型合法性 | 动态类型,继承关系和类型合法性在运行时确定 |
访问控制 | 通过public 、protected 、private 严格限制成员的访问权限 |
无严格访问控制,依赖命名约定(如_name 表示私有) |
继承方式 | 支持单继承、多重继承,但多重继承需谨慎处理菱形继承问题 | 天然支持多重继承,通过方法解析顺序(MRO)解决冲突 |
(2) 多态的实现方式
• C++ :需显式声明虚函数(virtual
),通过虚函数表(vtable)实现动态绑定。
cpp
class Base {
public:
virtual void func() { /* 基类实现 */ }
};
class Derived : public Base {
public:
void func() override { /* 子类重写 */ }
};
• Python:所有方法默认可被重写,无需关键字修饰,运行时动态绑定。
python
class Parent:
def func(self):
print("父类方法")
class Child(Parent):
def func(self):
print("子类重写方法")
(3) 内存管理与生命周期
• C++ :基类析构函数需声明为虚函数(virtual ~BaseClass()
),确保子类对象析构时正确调用链式析构。
• Python:依赖垃圾回收机制(GC),无需手动管理内存,父类和子类对象的生命周期由引用计数自动处理。
(4) 构造函数的调用
• C++:子类构造函数需显式调用基类构造函数(通过初始化列表):
cpp
class Derived : public Base {
public:
Derived() : Base() { /* 子类构造逻辑 */ }
};
• Python :子类通过super().__init__()
调用父类构造函数:
python
class Child(Parent):
def __init__(self):
super().__init__() # 调用父类构造函数