Python和C++的区别(执行、内存管理、多态)

  • Post author:
  • Post category:python




执行

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操作的机制:

  1. 调用

    operator new

    标准库函数,分配足够大的原始的没有初始化的内存空间;
  2. 执行该类型的

    构造函数

    ,在上一步分配的内存上初始化对象;
  3. 返回指向新分配并构造了新对象空间的指针。

delete操作的机制:

  1. 对该指针指向的对象调用

    析构函数

  2. 调用

    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()



版权声明:本文为weixin_37711353原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。