python类与c++类对比

写在前面

本文将从类的角度讨论python和c++在语法层面和使用层面的不同

主要内容

语法方面:

先看c++类的一个简单的例子:

class A {
public:
    int i;
    void test() {
        std::cout << i << endl;
    }
};

.....
A a;
a.i = 100;
a.test();

输出100

这个A类很简单,只有一个数据成员i和一个test函数用于输出i的值。
但是从语法上当我们以实例对象a调用test成员函数的时候,这个函数内部是如何找到这个i的?也就是说当执行test这段函数的代码的时候如何找到这个和a对象绑定的i的?

原因很简单就是我们经常说的this指针。c++的编译器会将上述的代码加工出现以下的半成品:

class A {
public:
    int i;
    void test(this) {
        std::cout << this->i << endl;
    }
};

.....
A a;
a.i = 100;
a.test(&a);

编译器会将默认的this指针当做非静态成员函数的一个默认的参数,帮我们添加上去,在使用对象调用的时候将对象的地址传递进去,在成员函数内部对类内部的成员都会添加上this->调用,这样就很容易区分不同对象的调用访问的是各自对象的空间的内容。

c++ 的编译器帮助我们完成this指针的传参,添加修饰的工作,不需要我们去写。

对于这个情况python是如何处理的呢?

如果按照c++的方式将得到以下的代码:

class A:
    i = 10;
    def test():
        print(i)

a = A()

a.test()

以上的代码是无法通过的,首先一个问题就是在方法的调用上出错。执行报错会显示,test()实际上是没有参数的但是我们传递给他了1个参数。这是为什么?

原因在于如果你是采用对象.方法的方式调用对象的方法python也会像c++一样帮你传入一个标识是哪个对象调用的变量叫做self,这个self是当前对象a的引用,也就是他也指向a所指向的内存空间上的那个对象。虽然看起来我们什么参数也没有传递但是python的解释器也会帮我们做这样的事情以协助其区分不同的对象。而上面这段代码在定义test函数的时候就没有给test写上self参数,结果我们也可以看出python虽然帮助我们在调用的时候传入引用给self但是其并没有在定义的时候帮助我们添加这个参数,这和c++是不一样的。更改之后我们得到如下的版本:

class A:
    i = 10;
    def test(self):
        print(i)

a = A()

a.test(

执行这段代码之后还是报错:在test函数当中并没有找到变量i的定义。这又是为什么的?

还是同样的原因,因为在定义类的方法的时候,python没有帮助我们添加self在变量i的前面,当然self是test的参数这个self是约定俗成的名称,可以换,所以既然在函数的参数当中python就没有给你添加就自然也不会再函数内部添加了。所以如果这个i指的是类当中定义的变量那么就必须加上self.修饰告诉python解释器。
最终修改后得到版本:

class A:
    i = 10;
    def test(self):
        print(self.i)

a = A()

a.test(

成功的输出了10。

python和c++在类对象调用方法上都是采用传入实例对象的指针或者引用的方式区分不同的调用对象的,但是在实际的语法形式上是略有不同的,尤其是python,这一点需要注意。

一个有意思的现象:

以下这段代码会输出什么?1还是2?买定离手~

class Test(object):
  i = 1
  def test_print(self):
    print(i)

t = Test()
i = 2
t.test_print()

答案是输出2。

反正我第一次看到是想错了,对于用c++比较多的我来说先入为主的观念还是比较的重的,首先这个i是没有加self.修饰的这个i就不是指类Test当中定义的那个i,所以当对象t调用test_print方法的时候虽然将t的引用传给了self但是此时在函数的内部是没办法精确找到这个i的,因为这个i在函数内部是没有被定义的。

此时就要扯到python的另一个语言特性了,访问引用的方式:
对于一个引用符号python有两种访问的方式
- 直接引用:直接使用引用的名字进行访问,Python按照搜索LEGB作用域的方式搜索名字。
- 间接访问:使用objname.attrname的方式引用名字attrname,Python不搜索作用域,直接去对象里找属性。

上述的方式就是对i进行直接访问!直接访问是怎么访问的?不知道的可以看这篇博客:ython进阶_关于命名空间与作用域(详解)

总之这个i是往函数外层的嵌套作用域去寻找这个变量的定义自然就在全局区域找到这个i,所以输出的是2。

上面这个性质是个坑,大家小心避雷。

python 当中的方法和函数的概念是c++当中不一样的概念。

c++类的成员函数分为静态成员函数和普通成员函数(先不考虑访问限定)。通过类名可以直接访问静态的成员(变量和函数),但是不能访问普通的成员和函数,普通的函数和成员是属于对象的需要this指针才能调用,而类名是没有的。

对于python,是没有静态非静态的概念的,python的类当中定义的函数,通过实例调用就是实例的方法,通过类名调用就是一个函数。函数和方法不是一个类型。方法是被实例对象调用的,会被默认传入一个对象本身的引用给self参数,而通过类名调用的是从属于类的函数对象,这个函数在被调用的时候需要显示的传入实例对象作为参数。这两种调用的方式产生的结果是一样的。x.f() 等价于 MyClass.f(x)。

python当中类是一个抽象的对象,实例是类对象。python当中一切皆对象所以类本身也是对象,c++当中类就是一段抽象的代码段,类并不是对象,c++当中的类是一个作用域,通过这个作用域只能访问到静态的信息。python当中的类就是一个抽象对象,他描述整个类的抽象信息。
做一个测试:

class A:
    pass
a = A()
print(type(A))
print(type(a))

输出
<class 'type'>
<class '__main__.A'>

输出的结果我们可以看出 类A的类型是一个类型类,a的类型是A类。也就是说A这个类型对象是包含类信息的对象他的使用自然和实例对象是不同的。

构造函数,析构函数是c++类的特色,c++编译器会给类添加默认的无参构造函数,拷贝构造函数等等,python呢?

python并没有构造函数析构函数这一概念毕竟内存管理是交给GC的不是程序员管理的,但是python有一个默认的初始化方法__init__,默认python会提供一个无参的__init__方法(这里无参不包含self),但是我们可以改写这个方法,定义自己的方法,添加参数进行合理的初始化,但是切记python只有重写没有重载,一个变量名只能引用一个对象,所以使用相同的符号根据参数不同调用不同的函数在python当中是不存在的!!!所以一旦重写原来的就没有了!!!而c++当中则不是虽然我们一旦重载了构造函数编译器就不提供无参构造函数,但是没关系我们还可以继续重载写多少个随意。

值得一提的是这个__init__ 方法的调用和c++调用构造函数是一样的都是在实例化对象的时候通过类的名字调用的。

c++的继承和python的继承有啥不一样?

一个有 “class” 的语言如果没有继承就没有多大的价值了,所以python当然支持继承,不但支持继承还支持多继承这和c++ 是一样的不像Java果断的舍去了类的多继承开辟接口去了。

python的继承语法如下:

class DerivedClassName(BaseClassName):
    <statement-1>
    .
    .
    .
    <statement-N

BaseClassName 的定义对于派生类而言必须是可见的.

python的继承有几个特点语法上和c++略有不同,同时在派生类对象调用方法的时候和c++是一样的先看派生类本身有没有定义没有就去父类找。
其他的性质和c++基本一样。

Python 支持多重继承. 一个多重继承的类定义看起来像这样:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

python当中所有的类都是从object派生的,c++是没有这样的一种概念的因为c++并不是一个纯面向对象的语言,而python和Java一样通过一个所有类的基类来抽象出所有类的抽象信息方便用户使用也方便实现一些高级的机制。这是c++所不具备的高级的机制,但是这样的机制也使得这些语言需要给每个对象付出额外的空间去保存这些信息同时也使得执行的效率降低。

对于访问控制两者的区别在哪里?

c++自不用多说,可以通过public private protected 这样的访问控制关键字来限定成员的访问权限。python当中并没有这样的限定,并不存在那种无法访问的私有的变量,但是python当中有一个约定:

以一个下划线带头的名字 (如 _spam) 应该作为非公共的 API (不管是函数, 方法或者数据成员). 这应该作为具体的实现, 而且变化它也无须提醒.

也就是说这样的成员你想访问还是可以访问到也可以修改,但是他本身不是提供给外界使用的只是实现这个类的人员为了实现某些功能时定义的子函数或者变量。

python类的特殊性质

python毕竟是动态语言,可以动态往对象里添加属性,这是python作为高级语言的特色是c++类所不具备的。往对象你添加的属性属于对象。

class A:
    pass


a = A();

a.name = "kobe"
a.age = 40
a.like = "basketball"

迭代器

这就比较的有意思了,c++的类是不支持迭代器,c++只有STL当中的容器是支持迭代器的,迭代器就是一种封装了指针的智能指针,提供遍历容器的一种方式。在c++当中迭代器是属于容器的一个概念。

Python当中呢,迭代器不仅可以作用于容器还可以作用于类。你还可以自定义遍历一个类对象时迭代器迭代的方式得到的结果。而且作为高级语言python将迭代器直接嵌入到for …in…的语法当中。
具体语法看文档

  • 9
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值