python和c++的一些区别(一)

python和c++的一些区别(一)
变量存储的机制上

众所周知,python没有c/c++里类似i++和++i这样语法。c/c++中,用户可以使用i++和++i进行整型变量的自增和数据结构的迭代器的自增。而i++和++i的区别在于,i++会返回一个自增前变量的拷贝副本,这个副本被存放在一个新的位置,而自增后的变量数值会被放回原来的地址,副本和变量本身无直接关系。

与c++不同,python中,所有一切都被视为对象,且分为不可变对象和可变对象。

1.可变对象:大多数的容器类,包括列表、字典、集合和用户定义的对象。

2.不可变对象:包括整数、浮点数、字符串、元组和冻结集合。

可变对象很好理解,可以在对象元素的对应位置修改内容,而不具体改变对象所在地址,即不需要创建新的对象。实际上,python的列表对象就就是由一堆指向元素的指针和一些其他功能函数组成。需要注意的是,由于python对内存显式控制不敏感,python中的一个int对象所占空间会远大于在c中的int变量,其他的一些基本类型也是如此。以后可能要专门开一篇挖掘下。

不可变对象就比较麻烦,因为它本身不可被修改的特性,使得在对它进行任何“修改”操作时,进行得不是实际得修改,而是重新绑定,这与C++中的移动语义有些相似。这使得虽然看上去能对对象进行切片操作,但实际上不能修改。于是,你会在python见到如此场景。

与c中每个变量分配一个地址不同,i1和i2是分别注册的整型变量,两者却地址相同。这就是python设计不可变对象的初衷之一,使用对象缓存优化对常量进行保存,包括小整数缓存(CPython中规定范围是-5至256,但实际会远超这个范围)、字符串驻留、函数默认参数缓存、方法解析缓存等等。因为它们常常会在程序中频繁被使用,使用该方法可以减小内存占用。值得注意的是,True、False、None(尺寸:28B、24B、16B)这些常用布尔值也是有被缓存的,在python程序以单例模式存在。

插一句,python里0,1两个常量对于False,True是特殊的,0,1两个常量可被直接视作布尔值0,1,可以被直接用在判断句子里,而其他数据或者数据结构均不可。其他数据或数据结构,未初始化时布尔值固定为0(False),初始化(not None)后,布尔值为1(True),这点与C++相同。

设计不可变对象的还有一个好处是安全性。在普通的拷贝对象(值传递)时,原对象和拷贝对象可以“指向”同一个地址,减少内存占用。而在其中一个对象改变时,保留另一个对象的指向不变,创建一个新的对象存储,不需要担心其他绑定在不可变对象上的其他对象被修改。相比之下,C/C++中,每次拷贝数据结构或对象时,拷贝对象(数据结构)都会存储在一个新的地址,会占用更多的空间。不过python的这种转移是隐性的,有时会造成一些问题。例如自增时,对象地址会不断变化,后面需要判断一个变量是否是原来的那个变量;向函数传递变量时,如果传入的实参是不可变对象,那么在函数内对该变量的写入操作都只会作用在函数内创建的局部变量上

image-20250909190510773

python与c/c++在变量自增下存储区别

c++和python支持将字面量、自定义对象、全局对象作为占位的默认参数。其中,有一种“默认参数陷阱”也是值得注意的。python中,在默认参数未修改前,其指向全局变量空间/静态变量空间,故参数初始化的会被放在静态变量空间中,而不是栈空间。如果是不可变对象那很ok,但如果是可变对象,会导致对其的写入操作在函数退出后会被保存,这种隐式操作常常会导致预料外的结果,既不报错,也不停止。这种函数设计也是不符合python规范的。

C++中默认参数可以是NULL、nullptr、字面量、字符串、自定义对象(可见的变量)。但需要注意基本数组和字符串作为默认参数时,数组在作为参数时会退化成指针,无法将数组字面量传给指针,字符串则是不能进行修改。如果要使用数组和字符串作为默认参数,最好使用stl容器array、vector、string等或者自定义容器。

python在类的继承和多态方面与c/c++中又有许多不同,这也是因为python的设计哲学更加重视“方法实现”,而不在意“对象类型”,不过留到下次再写吧。