菜单

忘记的时候看看,继承和python继承不同点

2020年1月19日 - 4166m金沙
忘记的时候看看,继承和python继承不同点

class A
{
public:
void f1()
{
cout << “A.f1()” << endl;
}

今日看了的,以为必要通晓对象内部存储器的难点。参照他事他说加以考察:

这么一大堆名词,实际上就围绕后生可畏件事张开,就是多态,别的四个名词都以为兑现C++的多态机制而提议的局地规规矩矩,下边分两有的介绍,第黄金年代局地介绍【多态】,第二片段介绍【虚函数,纯虚函数,抽象类】

void f()
{
f1();
//f3();
}
};

1.何为C++对象模型?

一 【多态】

class B:public A
{
void f1()
{
cout << “B.f1()” << endl;
}
void f3()
{
cout << “B.f3()” << endl;
}
};

援引《深度索求C++对象模型》那本书中的话:

多态的概念 :关于多态,好两种说法,好的坏的都有,分别说一下:

B b;
b.f();
出口为A.f1(卡塔尔国,也等于说实行的是基类中的f1(卡塔尔国函数。假如python中贯彻均等的代码如下:
class A:
def f1(self):
print “A.f1”;
def f(self):
self.f1();
#self.f3();

  有多个概念能够表达C++对象模型:

  1. 言语中一贯扶持面向对象程序设计的风流洒脱对。

  2. 对此各个补助的底层达成机制。

直接扶植面向对象程序设计,包括了构造函数、析构函数、多态、虚函数等等,这么些内容在不少图书上都有切磋,也是C++最被人熟习的地点(个性)。而目的模型的底部实现机制却是非常少有图书斟酌的。对象模型的最底层达成机制未有规范化,不相同的编写翻译器有自然的随机来安顿指标模型的完成细节。在小编眼里,对象模型商讨的是指标在仓库储存上的半空中与时间上的更优,并对C++面向对象本领加以补助,如以虚指针、虚表机制协助多态性格。

1 指同三个函数的有余形状。

class B(A):
def f1(self):
print “B.f1”;
#A.f1(self);
def f3(self):
print “B.f3”;
b = B();
b.f();
运作输出却为B.f1

2.文章内容简要介绍

私家感到那是大器晚成种高手中的国手钟爱的传道,对于经常开辟职员是风流罗曼蒂克种差的不可能再差的定义,大约是对人的误导,然人比较轻松就靠到函数重载上了。

小结如下:
在C++中,public世袭注解A是B的后生可畏局地,在调用f(卡塔尔(قطر‎时,先在B类中找那几个函数,未有找到,到A类中找这些函数,找到调用A类中的那几个函数,那时候传到的this指针被转正为了A类对象的指针,这时的调用,找出函数都只会在A类的界定中,不会跳出A类。所以调用f1那么些函数的时候是调用A类中的f1。
在python中,父类和子类不是C++中父类和子类的涉嫌。能够将B类掌握成是叁个全新的类,只是说,那些类会包蕴A类的成员。而A类和B类都有f1那个函数,那时B类中定义的f1函数会将A类中定义的f1函数给覆盖掉。所以在调用f(卡塔尔函数时,f函数内部实际上调用的照旧B类中再一次定义的f1函数。

那篇小说首要来谈谈C++对象在内部存款和储蓄器中的构造,归属第贰个概念的商讨范围。而C++直接帮忙面向对象程序设计某些则相当少讲。文章首要内容如下:

 

以下是个人感到表明的可比好的三种说法,意思轮廓相似:

  1. 单世袭:子类单生龙活虎世袭自父类,解析了子类重写父类虚函数、子类定义了新的虚函数处境下子类对象内部存款和储蓄器布局。

  2. 多一而再:子类世袭于八个父类,剖析了子类重写父类虚函数、子类定义了新的虚函数景况下子类对象内部存款和储蓄器构造,同一时候深入分析了非虚世襲下的菱形世襲。

  3. 虚继承:深入分析了单纯世襲下的虚世襲、多种基层下的虚世襲、重复世袭下的虚世袭。

2多态是怀有表现各类样子的技术的风味,在OO中是指,语言具备依据指标的类型以区别措施管理之,特别是重载方法和世襲类这种形式的力量。

接头对象的内部存款和储蓄器构造之后,我们得以深入分析部分标题:

这种说法有个别绕,留神思谋,那才是C++要报告大家的。

  1. C++封装带给的构造费用是多大?

  2. 由空类组成的一而再档案的次序中,每种类对象的大小是多大?

 

有关其余与内部存款和储蓄器有关的学识,我豆蔻年华旦大家都有自然的询问,如内部存款和储蓄器对齐,指针操作等。本文初看大概晦涩难懂,供给读者有一定的C++基本功,对定义后生可畏有早晚的领会。

3多态性是同意你将父对象设置成为和多少个或更加多的她的子对象相等的技能,赋值之后,父对象就能够依照当前赋值给它的子对象的特点以分歧的格局运营。不问可见,正是一句话:允许将子类类型的指针赋值给父类类型的指针。多态性在Object
帕斯Carl和C++中都是通过虚函数(Virtual Function) 达成的。

 

这种说法看来是又易懂,又周全的风姿罗曼蒂克种,特别是最后一句,直接点出了虚函数与多态性的关联,假若您照旧不太懂,没涉及,再把3读三回,有个印象,现在看呢。

3.精通虚函数表

 

3.1.多态与虚表

二 【虚函数,纯虚函数,抽象类】

     
 C++中虚函数的效应至关心重视固然为着贯彻多态机制。多态,轻易的话,是指在再三再四档次中,父类的指针能够有所两种造型——当它指向某些子类对象时,通过它亦可调用到子类的函数,而非父类的函数。

多态才说了个概念,有何用尚未说就步向第三局地了?看看概念3的末段一句,虚函数正是为多态而生的,多态的效劳的牵线和虚函数差非常少关系太大了,就放一块说吧。

#include<iostream>
using namespace std;

class Base{public: virtual void print(void); };
class Drive1:public Base{ public:virtual void print(void); };
class Drive2 :public Base{public: virtual void print(void); };

int main()
{
Base ptr1 = new Base;
Base
ptr2 = new Drive1;
Base *ptr3 = new Drive2;

ptr1->print();
ptr2->print();
ptr3->print();
return 0;
}

 

那是后生可畏种运维期多态,即父类指针独有在程序运维时技艺知道所指的的确类型是哪些。这种运维期决议,是经过虚函数表来落到实处的。

多态的效率:继承是子类使用父类的艺术,而多态则是父类使用子类的不二诀窍。那是一句大白话,多态从用法上即是要用父类(确切的乃是父类的指标名卡塔尔(قطر‎去调用子类的法子,譬喻:

3.2.利用指针访谈虚表

【例一】

借使大家抬高咱们的Base类,使其有着多个virtual函数:

  class A {

class Base
{
public: 
    Base(int i) :baseI(i){};
    virtual void print(void){ cout << "调用了虚函数Base::print!"; }
    virtual void setI(){ cout << "调用的虚函数Base::setI!"; }
    virtual ~Base(){}
private:
    int baseI;
};

  public:

     
当叁个类本人定义了虚函数,或其父类有虚函数时,为了协助多态机制,编写翻译器将为此类加多二个虚函数指针(vptr)虚函数指针日常都坐落于对象内部存款和储蓄器布局的第贰个地点上,那是为了保证在多层世襲或多重世袭的情景下能以最高功能取到虚函数表。

  A() {}

     
当vprt坐落于对象内部存款和储蓄器最前边时,目的之处即为虚函数指针地址。大家能够收获虚函数指针的地址:

  (virtual) void print() {

Base b(1000);

int * vptrAdree = (int *)(&b);  

cout << “虚函数指针(vprt)的地址是:\t”<<vptrAdree
<< endl;

  cout << “This is A.” << endl;

我们运营代码出结果:虚函数指针vptr的地点是:        0018FB28

  }

 

  };

      大家强行把类对象的地址调换为 int*
类型,得到了虚函数指针之处。虚函数指针指向虚函数表,虚函数表中积累的是风姿浪漫多元虚函数的地址,虚函数地址现身的顺序与类中虚函数评释的顺序生龙活虎致。对虚函数指针地址值,能够博得虚函数表的地址,也等于虚函数表第叁个虚函数的地点:

  class B : public A {

typedef void(*Fun)(void);

    Fun vfunc = (Fun)*( (int *)*(int*)(&b));

    cout << “第多个虚函数之处是:” << (int
*)*(int*)(&b) << endl;

    cout << “通过地点,调用虚函数Base::print(卡塔尔国:”;

    vfunc();

  public:

 

  B() {}

  void print() {

像这种类型,我们就拿走了类中的第贰个虚函数,大家得以经过函数指针访谈它。

  cout << “This is B.” << endl;

 

  }

 运转结果:

  };

第四个虚函数的地址是:009CDC80

  int main(int argc, char* argv[]) {

经过地点,调用虚函数Base::print(卡塔尔(قطر‎:调用了虚函数Base::print!请按放肆键继续.
. .

      B b;

同理,第四个虚函数setI(卡塔尔之处为: 

A a;  a = b;a.print;—————————————- make1

(int * )(*(int*)(&b)+1)

// A &a = b; a->print();———————————-make2

 

 //A *a = new B();a->print();——————————–make3

风度翩翩致能够透过函数指针访谈它,这里留下读者本人考试。

  return 0;

到如今截至,我们知晓了类中虚表指针vprt的缘故,知道了虚函数表中的内容,以至如何通过指针访谈虚函数表。上面包车型客车篇章元帅常接受指针访谈对象内部存款和储蓄器来验证大家的C++对象模型,甚至探究在各个世袭情状下虚表指针的变动,先把那有的的从头到尾的经过消食完再接着看上边包车型地铁源委。

  }

 

  那将展现:

4.指标模型概述

  This is B.

在C++中,有二种多少成员(class data members):static
和nonstatic,以至三体系成员函数(class member
functions):static、nonstatic和virtual:

  如果把virtual去掉,将显示:

近些日子大家有四个类Base,它蕴涵了上面那5中项指标多少或函数:

This is A.

class Base
{
public:
    Base(int i) :baseI(i){};
    int getI(){ return baseI; }
    static void countI(){};
    virtual void print(void){ cout << "Base::print()"; }
    virtual ~Base(){}
private:
    int baseI;
    static int baseS;
};

(make1,2,3分别是对应异常准绳(前边介绍卡塔尔国的三种方法,调用结果是如出一辙的卡塔尔国

那就是说,那一个类在内部存款和储蓄器中校被什么表示?5种多少都以接二连三寄放的吧?怎么着架构技巧支撑C++多态?
我们的C++标准与编写翻译器将怎么着营造出种种数码成员与成员函数呢?

丰盛virtual
,多态了,B中的print被调用了,也便是能够达成父类使用子类的方式。

 

对多态的效果与利益有贰个起来的认知了以后,再建议更官方,也是更规范的对多态功用的叙述:

4.1.简易对象模型

多态性使得能够利用同生机勃勃类(基类卡塔尔(قطر‎类型的指针来援用不一样类的指标,甚至基于所引用对象的不一样,以不相同的方法试行相似的操作。把差异的子类对象都看作父类来看,能够屏蔽分化子类对象之间的异样,写出通用的代码,做出通用的编制程序,以适应需要的不仅改动。赋值之后,父对象就能够依据当下赋值给它的子对象的表征以分歧的艺术运营(也正是能够调用子对象中对父对象的相关函数的修改措施卡塔尔(قطر‎。

 

 

证实:在底下现身的图中,用朱红边框框起来的原委在内部存款和储蓄器上是连连的。

那么地点例子中缘何去掉virtual就调用的不是B中的方法了呢,明明把B的目的赋给指针a了哟,是因为C++定义了意气风发组对象赋值的万分准绳,便是指在国有派生的动静下,对于某个场所,二个派生类的指标足以作为基类对象来行使,具体来讲,正是上边两种情形:

 那个模型特别地大致暴虐。在该模型下,对象由意气风发多级的指针组成,每多个指南针都针对三个数额成员或成员函数,也就是说,每一个数据成员和分子函数在类中所占的尺寸是同等的,都为多个指针的深浅。那样有个平价——十分轻松算出目的的高低,可是赔上的是空大壮试行期效用。想象一下,假若大家的Point3d类是这种模型,将会比C语言的struct多了数不清上空来存放在指向函数的指针,并且每便读取类的数量成员,都亟需经过再一遍寻址——又是光阴上的损耗。所以这种对象模型并未被用来实际产品上。

 

                 图片 1 

Class A ;

4.2.表格驱动模型

class B:public A

   
  
以此模型在精短对象模型的功底上又增多贰个间接层,它把类中的数据分为了三个部分:数据部分与函数部分,并应用两张表格,一张寄放数据本人,一张存放函数的地址(也即函数比成员多贰次寻址),而类对象只是含有八个指针,分别指向上边那五个表。那样看来,对象的轻重缓急是确定地点为七个指针大小。这一个模型也还没用于实际使用于真正的C++编写翻译器上。

 

4.3.非世袭下的C++对象模型

1.    派生的靶子能够赋给基类的靶子

      概述:在那模型下,nonstatic
数据成员被放置每八个类对象中,而static数据成员被停放类对象之外。static与nonstatic函数也都放在类对象之外,而对于virtual
函数,则经过虚函数表+虚指针来支撑,具体如下:

A a;

B b;

先在VS上验证类对象的构造:

a = b;

Base b(1000);

2.    派生的对象足以初叶化基类的引用

 图片 2

B b;

可以知道对象b含有三个vfptr,即vprt。並且独有nonstatic数据成员被停放于对象内。咱们开展vfprt:

A &a = b;

图片 3 

3.       派生的目的的地点能够赋给指向基类的指针

vfptr中有七个指针类型的数码(地址),第一个针对了Base类的析构函数,第一个针对了Base的虚函数print,顺序与注脚顺序雷同。

B b;

 

A *a = &b;

这与上述的C++对象模型相符合。也足以因而代码来进展验证:

void testBase( Base&p)

{

    cout << “对象的内部存款和储蓄器初始地址:” << &p << endl;

    cout << “type_info信息:” << endl;

    RTTICompleteObjectLocator str =
*((RTTICompleteObjectLocator*)*((int*)*(int*)(&p) – 1));

 

    string classname(str.pTypeDescriptor->name);

    classname = classname.substr(4, classname.find(“@@”) – 4);

    cout <<  “根据type_info新闻输出类名:”<< classname
<< endl;

    cout << “虚函数表地址:” << (int *)(&p) << endl;

    //验证虚表

    cout << “虚函数表第贰个函数之处:” << (int
*)*((int*)(&p)) << endl;

    cout << “析构函数的地点:” << (int* )*(int
*)*((int*)(&p)) << endl;

    cout << “虚函数表中,第三个虚函数即print()的地点:”
<< ((int*)*(int*)(&p) + 1) << endl;

 

    //通过地点调用虚函数print()

    typedef void(*Fun)(void);

    Fun IsPrint=(Fun)* ((int*)*(int*)(&p) + 1);

    cout << endl;

    cout<<“调用了虚函数”;

    IsPrint(卡塔尔; //若地址准确,则调用了Base类的虚函数print()

    cout << endl;

 

    //输入static函数的地方

    p.countI(卡塔尔(قطر‎;//先调用函数以发出三个实例

    cout << “static函数countI()的地址:” << p.countI
<< endl;

 

    //验证nonstatic数据成员

    cout << “估摸nonstatic数据成员baseI的地点:” << (int
*)(&p) + 1 << endl;

    cout << “根据测算出的地点,输出该地方的值:” <<
*((int *)(&p) + 1) << endl;

    cout << “Base::getI():” << p.getI() << endl;

}

Base b(1000);

testBase(b);

A *a = new B();

 图片 4

由上述目的赋值包容法规能够,多个基类的靶子可宽容派生类的对象,叁个基类的指针可针对派生类的靶子,叁个基类的援用可援用派生类的目的,于是对于经过基类的靶子指针(或援用State of Qatar对成员函数的调用,编写翻译时束手待死分明目的的类,而只是在运行时才干明确并透过分明调用哪个类中的成员函数。

结果剖判:

探访刚才的例证,遵照宽容准则,B的对象根本就被当成了A的靶子来行使,难怪B的措施无法被调用。

【例二】

好的,至此大家精通了非世袭下类对象四种多少在内部存款和储蓄器上的构造,也掌握了在每叁个虚函数表前都有贰个指南针指向type_info,担负对RTTI的协理。而步向世袭后类对象在内存中该怎么表示呢?

#include <iostream>

 

using namespace std;

5.世袭下的C++对象模型

class A

 

{

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图