Python VS C++
执行
Python | C++ | |
---|---|---|
语言类型 | 脚本语言 | 编程语言 |
运行方式 | 解释器翻译成可识别的指令,解释运行 | 编译链接,形成可执行文件 |
执行效率 | 较低 | 很高 |
语法 | 动态 | 严格 |
平台 | 可跨平台 | 不可跨平台 |
优点 | 方便快捷,轻量级开发 | 安全稳定 |
内存管理
Python内存管理
引用计数
Python中的赋值即对对象的引用。对对象的引用类似C++中的指针,因此变量的复制就是多个变量指向同一个对象:
a = 3
b = 3
print(id(a) == id(b)) # True
print(a is b) # True
但只有对
不可变对象
(int、bool、float、tuple、string等)的多个引用才是指向同一个对象(内存空间),对可变对象(list、object等)的多个引用是分配不同的内存空间:
a = []
b = []
print(id(a) == id(b)) # False
print(a is b) # False
在Python内部使用引用计数,追踪内存中的对象有多少引用,当引用数为0时,被回收。可以通过
sys
模块中
getrefcount()
获取对象的引用数:
from sys import getrefcount
a = [1, 2, 3]
print(sys.getrefcount(a)) # 2
b = a
print(sys.getrefcount(a)) # 3
print(sys.getrefcount(b)) # 3
由于
getrefcount()
传参时引用数加一,所以返回的值会比实际引用数
多1
垃圾回收
当一个内存块(变量)的引用计数为0会被回收。在Python中,会记录
object allocation
和
object deallocation
的次数,当差值到达某阈值时,启动垃圾回收:
import gc
print(gc.get_threshold()) # (700,10,10) 700即为该阈值
另外,Python GC当发现某些引用是循环引用后,会将这些引用的计数器
多减一个1
。所以这些循环引用指向的空间仍然会被释放。
内存池机制
Python引入内存池(memory pool)机制,即Pymalloc,对小块内存的分配和释放进行管理。内存池的概念就是预先在内存中申请一定数量的,大小相等的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给这个需求,不够了之后再申请新的内存。析构时也采用了内存池机制,从内存池申请到的内存会被归还到内存池中。
-
当内存大小**<256k**:使用
Pymalloc
实现的分配器在内存池中申请内存空间; -
当内存大小**>256k**:使用系统的
malloc
申请内存空间。
另外Python对象(int、float、list等),都有其独立的私有内存池,对象间不共享他们的内存池。
金字塔
- 第3层:最上层,用户对Python对象的直接操作
- 第1层和第2层:内存池,由Pymalloc管理
- 第0层:大内存,malloc函数分配内存,free函数释放内存
- 第-1,-2层:操作系统进行操作
C++内存管理
内存分块
内存分区 | 存放内容 | 管理 | 生命周期 |
---|---|---|---|
栈 | 函数的局部变量 | 编译器负责分配、释放 | 函数内部 |
堆 | new申请的内存 | new分配,delete释放 | new-delete之间 |
自由存储区 | malloc申请的内存 |
malloc/calloc/realloc分配,free释放;若忘记free,可能会有 内存泄漏 ,会由OS回收 |
malloc-free(若忘记free,程序结束) |
全局/静态存储区 | 全局变量、静态变量 | 编译器负责分配、释放 | 编译完成-程序结束 |
常量存储区 | 不能修改的常量 | 编译器负责分配、释放 | 编译完成-程序结束 |
new 和delete
new和delete不是函数,是C++中定义的关键字。通过重载operator new函数和operator delete函数,来实现对某类型对象的新建/析构。
new操作的机制:
-
调用
operator new
标准库函数,分配足够大的原始的没有初始化的内存空间; -
执行该类型的
构造函数
,在上一步分配的内存上初始化对象; - 返回指向新分配并构造了新对象空间的指针。
delete操作的机制:
-
对该指针指向的对象调用
析构函数
; -
调用
operator delete
标准库函数释放该对象所用的内存。
malloc和free
malloc和free是标准库函数。malloc和free函数维护一个空闲
链表
,链接可用的内存块。
调用malloc函数时,它沿链表寻找一个大到足以满足用户请求所需要的内存块。将分配给用户的那块内存传给用户,并将剩下的那块(如果有的话)返回到链表上。
调用free函数时,它将用户释放的内存块连接到空闲链上。
到最后,空闲链会被切成很多的小内存片段,如果这时用户申请一个大的内存片段,那么空闲链上可能没有可以满足用户要求的片段了,需要对小内存块进行合并。
如果无法获得符合要求的内存块,malloc函数会返回
NULL指针
,因此在调用malloc动态申请内存块时,
一定要进行返回值的判断
。
多态
多态即一个事物的多种形态。类多态是指
不同的子类
对象调用
相同的父类
方法,产生不同的执行结果,可以增加代码的外部调用灵活度,多态以
继承
和
重写
父类方法为前提,不会影响到类的内部设计。
C++中多态和虚函数
虚函数:基类可以有定义,子类可以没有定义,可以不重载;
virtual void read();
纯虚函数:基类不能有定义,子类必须有定义,必须重载;
virtual void read() = 0;
两者都是”运行时多态”,指一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻根据虚函数表确定的。
Python中多态和虚函数
Python中变量的使用不用声明,所以不存在父类引用指向子类对象的多态体现,同时python不支持重载。在Python中,利用decorator通过添加
@abstractmethod
定义虚函数以实现多态:
from abc import ABCMeta, abstractmethod
class Base():
__metaclass__ = ABCMeta
def __init__(self):
pass
@abstractmethod
def get(self):
print "Base.get()"
pass
class Derive1(Base):
def get(self):
print "Derive1.get()"
class Derive2(Base):
def get(self):
print "Derive2.get()"
if __name__ == '__main__':
b = Derive1()
c = Derive2()
b.get() # Derive1.get()
c.get() # Derive2.get()