精华内容
下载资源
问答
  • 虚基类和多重继承什么是多重继承多重继承的优点关于菱形继承的问题其他多重继承的情况如何解决多重继承的问题什么是虚基类虚基类如何解决问题查看虚基类的内存布局对比普通继承下的内存布局 什么是多重继承 多重继承...

    什么是多重继承

    多重继承,很好理解,一个派生类如果只继承一个基类,称作单继承
    一个派生类如果继承了多个基类,称作多继承
    如图所示:
    在这里插入图片描述

    多重继承的优点

    这个很好理解:
    多重继承可以做更多的代码复用!
    派生类通过多重继承,可以得到多个基类的数据和方法,更大程度的实现了代码复用。

    关于菱形继承的问题

    凡事有利也有弊,对于多继承而言,也有自己的缺点。
    我们先通过了解菱形继承来探究多重继承的缺点:
    菱形继承是多继承的一种情况,继承方式如图所示:
    在这里插入图片描述
    从图中我们可以看到:
    类B类C类A单继承而来;
    类D类B类C多继承而来。
    那么这样继承会产生什么问题呢?
    我们来看代码:

    class A
    {
    public:
    	A(int data) :ma(data) { cout << "A()" << endl; }
    	~A() { cout << "~A()" << endl; }
    protected:
    	int ma;
    };
    class B :public A
    {
    public:
    	B(int data) :A(data), mb(data) { cout << "B()" << endl; }
    	~B() { cout << "~B()" << endl; }
    protected:
    	int mb;
    };
    class C :public A
    {
    public:
    	C(int data) :A(data), mc(data) { cout << "C()" << endl; }
    	~C() { cout << "~C()" << endl; }
    protected:
    	int mc;
    };
    class D :public B, public C
    {
    public:
    	D(int data) : B(data), C(data), md(data) { cout << "D()" << endl; }
    	~D() { cout << "~D()" << endl; }
    protected:
    	int md;
    };
    int main()
    {
    	D d(10);
    
    	return 0;
    }
    

    通过代码,我们将上述菱形继承完成了,接下来运行代码:
    在这里插入图片描述
    通过运行结果,我们发现了问题:
    对于基类A而言,构造了两次,析构了两次!
    并且,通过分析各个派生类的内存布局我们可以看到:
    在这里插入图片描述
    对于派生类D来说,间接继承的基类A中的数据成员ma重复了!
    这对资源来说是一种浪费与消耗。
    (如果多继承的数量增加,那么派生类中重复的数据也会增加!)

    其他多重继承的情况

    除了菱形继承外,还有其他多重继承的情况,也会出现相同的问题:
    在这里插入图片描述
    比如说图中呈现的:半圆形继承

    如何解决多重继承的问题

    通过分析我们知道了,多重继承的主要问题是,通过多重继承,有可能得到重复基类数据,并且可能重复的构造和析构同一个基类对象。
    那么如何能够避免重复现象的产生呢?
    答案就是:=》虚基类

    什么是虚基类

    要理解虚基类,我们首先需要认识virtual关键字的使用场景:

    1. 修饰成员方法时:产生虚函数
    2. 修饰继承方式时:产生虚基类

    对于被虚继承的类,称作虚基类
    比如说:

    class A
    {
    	XXXXXX;
    };
    class B : virtual public A
    {
    	XXXXXX;
    };
    

    对于这个示例而言,B虚继承了A,所以把A称作虚基类

    虚基类如何解决问题

    那么虚基类如何解决上述多重继承产生的重复问题呢?
    我们来看代码:

    class A
    {
    public:
    	A(int data) :ma(data) { cout << "A()" << endl; }
    	~A() { cout << "~A()" << endl; }
    protected:
    	int ma;
    };
    class B :virtual public A
    {
    public:
    	B(int data) :A(data), mb(data) { cout << "B()" << endl; }
    	~B() { cout << "~B()" << endl; }
    protected:
    	int mb;
    };
    class C :virtual public A
    {
    public:
    	C(int data) :A(data), mc(data) { cout << "C()" << endl; }
    	~C() { cout << "~C()" << endl; }
    protected:
    	int mc;
    };
    class D :public B, public C
    {
    public:
    	D(int data) : B(data), C(data), md(data) { cout << "D()" << endl; }
    	~D() { cout << "~D()" << endl; }
    protected:
    	int md;
    };
    

    我们做出修改:
    BC的继承方式都改为虚继承;接下来继续运行代码:
    此时会报错:
    在这里插入图片描述
    提示说:"A::A" : 没有合适的默认构造函数可用
    为什么会这样呢?
    我们可以这么理解:
    刚开始BC单继承A的时候,实例化对象时,会首先调用基类的构造函数,也就是A的构造函数,到了D,由于多继承了BC,所以在实例化D的对象时,会首先调用BC的构造函数,然后调用自己(D)的。
    但是这样会出现A重复构造的问题,所以,采用虚继承,把有关重复的基类A改为虚基类,这样的话,对于A构造的任务就落到了最终派生类D的头上,但是我们的代码中,对于D的构造函数:D(int data) : B(data), C(data), md(data) { cout << "D()" << endl; }并没有对A进行构造。
    所以会报错。
    那么我们就给D的构造函数,调用A的构造函数:
    D(int data) :A(data), B(data), C(data), md(data) { cout << "D()" << endl; }
    这一次再运行:
    在这里插入图片描述
    我们会发现,问题解决了。

    查看虚基类的内存布局

    我们上面只是介绍了可以通过虚基类的方法来解决多重继承产生的问题,
    但是并不知道虚基类具体是如何做的,为什么使用它就能解决上述问题。
    为了更好地理解虚基类的工作原理,我们可以通过工具来查看它的内存布局:
    通过VS工具 => 命令行 => 开发者命令提示(C)
    在这里插入图片描述
    接下来切换到当前源文件的目录下:
    在这里插入图片描述
    (注意:当前源文件的目录可以通过右键当前源文件=>打开所在的文件夹(O)=>复制目录)
    接着输入命令:cl XXX.cpp /d1reportSingleClassLayoutX(第一个XXX表示源文件的名称,我这里就是继承与多态.cpp,第二个X表示想要查看的类的名称,我这里就是B)
    在这里插入图片描述
    我们可以看到当前B的内存空间:
    在这里插入图片描述
    当前B的内存空间里,前四个字节是vbptr(这个就代表里虚基类指针:virtual base ptr);
    vfptr(虚函数指针)指向了vftable(虚函数表)一样,
    vbptr(虚基类指针)指向了vbtable(虚基类表)。
    vbtable(虚基类表)的布局也如图所示,
    首先是偏移量0:表示了虚基类指针再内存布局中的偏移量;
    接着是偏移量8:表示从虚基类中继承而来的数据成员在内存中的偏移量。

    对比普通继承下的内存布局

    我们可以对比没有虚继承下的B的内存布局来理解:
    在这里插入图片描述
    我们把他们放在一起对比可以看到:
    在这里插入图片描述
    继承虚基类的类(BC)会把自己从虚基类继承而来的数据ma放在自己内存的最末尾(偏移量最大),并在原来ma的位置填充一个vbptr(虚基类指针),这个指针指向了vbtable(虚基类表)。
    理解了B,我们可以看看更为复杂的D
    在这里插入图片描述
    同样的,和为使用虚基类前的内存布局进行对比:
    在这里插入图片描述
    可以看到,将ma移动到了末尾处,并在含有ma的地方,都用vbptr进行填充。
    这样一来,就只有一个ma了!解决了多重继承的重复问题。

    展开全文
  • 一:什么是C++多继承义性?先看以下的代码:class A{public:int iValue;};class B1:public A{public:void b1Printf() {cout<};class B2:public A{public:void b2Printf() {cout<};class C:public B1,public ...

    一:什么是C++多继承的多义性?

    先看以下的代码:

    class A

    {

    public:

    int iValue;

    };

    class B1:public A

    {

    public:

    void b1Printf()  {cout<

    };

    class B2:public A

    {

    public:

    void b2Printf()   {cout<

    };

    class C:public B1,public B2

    {

    public:

    void cPrintf()   {cout<

    };

    void main()

    {

    C c;

    cout<

    cout<<:ivalue>

    cout<<:ivalue>

    cout<<:ivalue>

    }

    分析:

    如两个类B1,B2继承自同一个类A,然后又派生出同一个新类C.则用类C创建对象的objC访问父类A的成员,

    就会出现多义性:即编译器无法确定访问的是通过类B1还是类B2的接口来访问类A的成员.

    也就是说类B1, B2都继承了类A的iValue成员,因此类B1,B2都有一个成员变量iValue ,这样类C继承类B1,B2后就有一个重名的成员 iValue(一个是从类B1中继承过来的,一个是从类B2中继承过来的).在调用时编译器不知道调用从谁继承过来的iValue所以就产生的二义性的问题.

    二:多继承的多义性的解决方法:

    1.通过作用域分辨符::指明调用类B1,B2的iValue成员副本,此时类A可以不指定为虚基类.(此时创建类C的对象的话类A的构造函数只出现两次).

    2.将在不同的派生路径中产生多个成员副本的基类设置为虚基类.此时在内存中,其成员就只有一个副本或者映射,解决了同名成员的唯一标识问题.

    也就是说在继承的类A的前面加上virtual关键字表示被继承的类A是一个虚基类,它的被继承成员iValue在派生类B1,B2中只保留一个实例。(此时创建类C的对象的话类A的构造函数只出现一次)

    两种方法的选择使用:

    1.使用作用域分辨符时,基类成员在内存中有多个副本,可存放不同的数据,进行不同的操作.

    2.使用虚基类技术,基类成员在内存中只有一个副本,更节约空间.

    孰轻孰重,应酌情选择!

    三:虚继承与虚基类:

    先看代码:

    class A

    {

    public:

    int iValue;

    };

    class B1 :  virtual  public A

    {

    public:

    void b1Printf(){cout<

    };

    class B2 : virtual public A

    {

    public:

    void b2Printf(){cout<

    };

    class C:public B1,public B2

    {

    public:

    void cPrintf(){cout<

    };

    void main()

    {

    C c;

    cout<

    }

    1.虚继承与虚基类的定义:

    (1).虚继承(虚拟继承):在继承定义中包含了virtual关键字的继承关系;

    (2).虚基类:在虚继承体系中的通过virtual继承而来的基类,需要注意的是:struct CSubClass :  virtual public  CBase {}; 其中CBase称之为CSubClass的虚基类,而不是说CBase就是个虚基类,因为CBase还可以不是虚继承体系中的基类。

    2.虚继承和虚基类的语法:

    语法有语言的本身的定义所决定.

    class  CSubClass : virtual  public  CBaseClass {};

    3.虚继承和虚基类的语义:

    (1).关键字virtual的含义:

    不可以在语言模型中直接调用或体现的,但是确实是存在可以被间接的方式进行调用或体现的。

    比如:虚函数必须要通过一种间接的运行时(而不是编译时)机制才能够激活(调用)的函数,而虚继承也是必须在运行时才能够进行定位访问的一种体制。存在,但间接。

    (2).关键字virtual的特征:存在、间接和共享这三种特征。

    对于虚函数而言,间接性表明了其必须在运行时根据实际的对象来完成函数寻址,共享性表现在基类会共享被子类重载后的虚函数,其实指向相同的函数入口。

    对于虚继承而言,存在即表示虚继承体系和虚基类确实存在,间接性表明了在访问虚基类的成员时同样也必须通过某种间接机制来完成,共享性表现在虚基类会在虚继承体系中被共享,而不会出现多份拷贝。

    (3).关键字virtual的使用:

    用于虚继承和虚函数两种情况.

    (4),虚基类的初始化问题:

    (1).派生类的构造函数,可以完成基类的初始化工作,虚基类也不例外.

    (2).虚基类的初始化的特殊性只体现在其构造函数的调用顺序上:

    1).同一层次中,虚基类的构造函数先于非虚基类的调用,而多个虚基类之间其构造函数调用次序于生命顺序有关.

    2).不同层次之间,则按照"先基类,后自己"的次序调用.

    (3).在包含虚基类的多层次继承中虚基类的初始化问题:

    “为什么一旦出现了虚基类,就必须在每一个继承类中都必须包含虚基类的初始化语句”?

    虚基类是被共享的,也就是在继承体系中无论被继承多少次,对象内存模型中均只会出现一个虚基类的子对象(这和多继承是完全不同的),这样一来既然是共享的那么每一个子类都不会独占,但是总还是必须要有一个类来完成基类的初始化过程(因为所有的对象都必须被初始化,哪怕是默认的),同时还不能够重复进行初始化,那到底谁应该负责完成初始化呢?C++标准中选择在每一次继承子类中都必须书写初始化语句(因为每一次继承子类可能都会用来定义对象),而在最下层继承子类中实际执行初始化过程。所以上面在每一个继承类中都要书写初始化语句,但是在创建对象时,而仅仅会在创建对象用的类构造函数中实际的执行初始化语句,其他的初始化语句都会被压制不调用。

    示例代码:

    class A

    {

    public:

    A( int  i )

    {

    m_val = i;

    }

    int  m_val;

    };

    /*

    * 虚拟继承体系

    */

    class B1 : virtual public  A

    {

    public:

    B1(int  i) : A( i ) {}

    };

    class B2 : virtual public  A

    {

    public:

    B2(int  i) : A( i ) {}

    };

    class C : public B1, public B2

    {

    public:

    C( int  i ) : A( i ), B1( i ), B2( i ) {}

    };

    class D : public C

    {

    public:

    D( int  i ) : A( i ), C( i ) {}

    };

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

    {

    D objD(1);

    cout << "objD.m_val = " << objD.m_val << endl;

    return 0;

    }

    展开全文
  • 当一个类被派生类用virtual继承时,这个基类就叫做虚基类 c++中virtual有两个作用,一个是修饰函数,另外一个就是这里修饰继承方式 下面例子中A就是一个虚基类: class A{ public: protected: int ma; }; //...

    什么是虚基类:

    当一个类被派生类用virtual继承时,这个基类就叫做虚基类
    c++中virtual有两个作用,一个是修饰虚函数,另外一个就是这里修饰继承方式
    下面例子中A就是一个虚基类:

    class A{
    public:
    protected:
        int ma;
    };
    //虚继承,此时A被称为虚基类
    class B:virtual public A{
    public:
    protected:
        int mb;
    };
    

    虚基类和抽象类有什么区别:

    抽象类指的是,类中存在纯虚函数,即函数名=0这样的函数,可以定义指针和引用,但是不能实例化对象。
    实际上两个是完全不同的概念,一个被virtual修饰了继承方式,一个被virtual修饰了类的成员方法。

    什么是vbptr和vbtable:

    继承了虚基类A的B,此时B对象的内存布局上会多一个指针,叫做vbptr,保存的vbtable的地址
    在这里插入图片描述
    注意,按照正常逻辑来讲,布局应该是下面这样的,实际上是上图:
    在这里插入图片描述
    但是由于是虚继承,所以将虚基类的成员变量都放在内存布局最下面,然后在他本来在的位置上放一个vbptr,记录虚基类成员变量的偏移量,这样就能省去对象的空间

    那么vftable是什么用途呢:

    0:记录的是,vbptr向上的偏移量,这里是0字节
    8:指的是vbptr向下偏移8个字节,就可以访问到虚基类的成员变量.

    虚继承可能会有什么样的问题:

    class A{
    public:
         void print(){
            std::cout<<"A::print()"<<std::endl;
        }
    private:
        int ma;
    };
    
    class B:virtual public A{
    public:
        void print(){
            std::cout<<"B::print()"<<std::endl;
        }
    private:
        int mb;
    };
    
    int main(){
        A * p =new B();
        p->print();
        delete p;
        return 0;
    }
    

    运行后程序core dump掉:

    A::print()
    free(): invalid pointer
    已放弃 (核心已转储)
    

    报错原因大概可以猜出来,是对象在进行析构函数的调用时释放对象内存free()的参数不对,有问题,大概原因如下:

    按照通常情况下,p指向对象内存布局应该是这样的,实际不是:
    在这里插入图片描述

    现在由于是虚继承,出现了vbptr,导致内存布局调整,实际上这样的,p如果指向vbptr就不会有这样的问题:
    在这里插入图片描述

    自然就出现问题了。

    我们可以这样来看一下,new 和delete时的地址是不是不匹配:

    class A{
    public:
        void print(){
            std::cout<<"A::print()"<<std::endl;
        }
        void operator delete(void* p){
            
            cout<<"A operator delete p = "<<p<<endl;
            free(p);
        }
    private:
        int ma;
    };
    
    class B:virtual public A{
    public:
        void print(){
            std::cout<<"B::print()"<<std::endl;
        }
        void *operator new(size_t size){
            void *p = malloc(size);
            cout<<"B operator new p = "<<p<<endl;
            return p;
        }
    private:
        int mb;
    };
    
    int main(){
        A * p =new B();
        p->print();
        cout<<"main fun p = "<<p<<endl;
        delete p;
        return 0;
    }
    

    结果:

    B operator new p = 0x55df897cbeb0
    A::print()
    main fun p = 0x55df897cbebc
    A operator delete p = 0x55df897cbebc
    munmap_chunk(): invalid pointer
    已放弃 (核心已转储)
    

    栈上的对象不存在这样的问题,因为栈帧回退,内存就释放了。

    展开全文
  • Python3 抽象基类

    2021-02-04 02:21:33
    抽象基类抽象类是一个特殊的类,它的特殊之处在于只能被继承能被实例化。抽象类与普通类的不同之处在于:抽象类中要有抽象方法(没有实现功能),该类能被实例化,只能被继承,且子类必须实现抽象方法。疑问: ...

    抽象基类

    抽象类是一个特殊的类,它的特殊之处在于只能被继承,不能被实例化。

    抽象类与普通类的不同之处在于:抽象类中要有抽象方法(没有实现功能),该类不能被实例化,只能被继承,且子类必须实现抽象方法。

    疑问: 已经有了鸭子类型 和多态 ,为什么还要用这个呢?

    答: 为了 解决两个事情

    使用抽象基类的情况:

    1.某些情况下希望判断某个对象的类型

    2.强制子类必须全部实现抽象方法

    3.用于接口设计

    4.规范不同人的代码

    Python中并没有提供抽象类与抽象方法,但是提供了内置模块abc和collections.abc来模拟实现抽象类

    抽象基类的定义:

    由abc.ABCMeta这个元类实现的类就是抽象基类,如

    class BaseObj(metaclass=abc.ABCMeta):"""基类"""@abc.abstractmethoddefget(self, value):print(value)

    @abc.abstractmethoddefset(self, key, value):print(key, value)classDog(BaseObj):defget(self, value):print(value)returnvaluedefset(self, key, value):passdog=Dog()print(dog.get('fe_cow'))#输出结果:

    fe_cow

    fe_cow

    继承,使Dog类成为BaseObj的子类,不使用继承,可以用下面两种方法。

    register方法

    定义好的抽象基类通过register方法可以成为别的类的父类。

    抽象基类虽然可以成为别的类的父类,但是别的类并不会继承抽象基类的方法和属性

    importabc#定义一个抽象基类

    class AbstractClass(metaclass=abc.ABCMeta):pass

    #定义一个普通类继承自object

    classMyClass(object):pass

    #把我们定义的抽象基类注册为MyClass的父类

    AbstractClass.register(MyClass)

    mc=MyClass()print(issubclass(MyClass, AbstractClass)) #输出True

    print(isinstance(mc, AbstractClass)) #输出True

    #将我们定义的抽象基类注册到系统定义的类

    AbstractClass.register(list)print(isinstance([], AbstractClass)) #输出True

    __subclasshook__魔法方法

    看过上面的例子你们肯定会觉得,给每个类都注册一遍抽象基类太麻烦了,没错Python的开发者也这么觉得,于是__subclasshook__这个方法出现了

    几点说明:

    该方法定义在抽象基类中

    该方法必须定义为类方法

    该方法有三个返回值True: 如果测试类被认为是子类

    False: 如果测试类不被认为是子类

    NotImplemented: 这个后面讲

    class AbstractDuck(metaclass=abc.ABCMeta):

    @classmethoddef __subclasshook__(cls, subclass):

    quack= getattr(subclass, 'quack', None) #取出subclass的 quack 属性,如果不存在则返回 None

    ifcallable(quack):returnTruereturnNotImplementedclassDuck(object):defquack(self):pass

    classNotDuck(object):

    quack= "foo"

    print(issubclass(NotDuck, AbstractDuck))

    AbstractDuck.register(NotDuck)print(issubclass(NotDuck, AbstractDuck) )print(isinstance(NotDuck,AbstractDuck))print(issubclass(Duck, AbstractDuck))print(isinstance(Duck,AbstractDuck))

    结果:

    False

    True

    False

    True

    False

    示例代码中只是检查是不是子类。因为父类中也没有抽象方法,子类也没有实现,所以,isinstance值为false。

    collection.abc模块常用的抽象基类:

    __all__ = ["Awaitable", "Coroutine","AsyncIterable", "AsyncIterator", "AsyncGenerator","Hashable", "Iterable", "Iterator", "Generator", "Reversible","Sized", "Container", "Callable", "Collection","Set", "MutableSet","Mapping", "MutableMapping","MappingView", "KeysView", "ItemsView", "ValuesView","Sequence", "MutableSequence","ByteString",

    ]

    抽象类与接口

    什么是接口

    接口可以理解为自己给使用者来调用自己功能方法的入口。

    为什么要用接口

    (1)可以实现权限控制,比如可以通过接口做一下访问控制,可以允许或者拒绝调用者的一些操作。

    (2)降低了使用者的使用难度,使用者只需要知道怎么调用即可,不需要知道里边的具体实现方法。

    抽象类特点

    1.规定继承类必须具有抽象基类指定的方法

    2.抽象基类无法实例化

    以上两个特点,主要用于接口设计

    importabcclass Interface(metaclass=abc.ABCMeta):#定义接口Interface类来模仿接口的概念,python中没有interface关键字来定义一个接口。

    @abc.abstractmethoddef read(self): #定接口函数read

    pass@abc.abstractmethoddef write(self): #定义接口函数write

    pass

    class Txt(Interface): #文本,具体实现read和write

    defread(self):print('文本数据的读取方法')defwrite(self):print('文本数据的读取方法')class Sata(Interface): #磁盘,具体实现read和write

    defread(self):print('硬盘数据的读取方法')defwrite(self):print('硬盘数据的读取方法')

    抽象类与接口

    抽象类的本质还是类,指的是一组类的相似性,包括数据属性(如all_type)和函数属性(如read、write),而

    接口只强调函数属性的相似性。

    抽象类是一个介于类和接口直接的一个概念,同时具备类和接口的部分特性,可以用来实现归一化设计

    接口与归一化设计

    归一化让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了,这极大地降低了使用

    者的使用难度。

    归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合。

    定义一个抽象类的目的:就是为了可以规范不同人的代码

    使用抽象类注意的问题:

    抽象类中可以包含抽象方法,也可以包含具体方法

    抽象类中可以有方法,也可以有属性

    抽象类不能直接实例化

    子类可以不实现所有的抽象方法,这时子类则不能实例化。

    import abc

    class People(metaclass=abc.ABCMeta):

    # 定义一个抽象的方法

    @abc.abstractmethod

    def eat(self):

    pass

    #定义一个抽象类的方法

    @abc.abstractclassmethod

    def drink(cls):

    pass

    # 定义一个静态抽象方法

    @abc.abstractstaticmethod

    def work():

    pass

    展开全文
  • Python 用抽象基类避免继承错误,抽象基类(abstract base class,ABC)用来确保派生类实现了基类中的特定方法。本节将学习其优点以及如何使用Python内置的abc模块来定义抽象基类。那么抽象基类适用于哪些地方呢?前一...
  • 函数
  • 虚基类继承方式中虚基类成员另开辟空间。并是多态实现的机制。 函数,是基类中函数存在的方式。解决赋值兼容规则时,派生类对象代替基类对象时,基类指针可以访问派生类定义的基类的同名函数问题。实现...
  • //尼玛这都能行,被踢大了二、函数和一般函数函数就是加了vritual关键字的函数,引入函数的目的是为了实现态性(在此为运行时的态性),即可以通过父类的指针调用子类的对象,从而产生不同的...
  • 虚基类 为了避免多重继承时产生的二义性,使多重继承的同名成员仅保存一次,无其他副本占用内存。(使用作用于限定符可解决二义性,但仍会有个副本占用内存) 函数 函数是动态联编的基础,是实现多态的重要...
  • publicclassdemo3{publicclassInnerClassTest{classClothes{privateStringmaterial;privateintsize;privateStringcolor;publicStringgetColor(){returncolor;}publicvoidsetColor(Str...public class demo3 {public ...
  • 实例讲解C++编程中的函数与虚基类虚函数①#include "stdafx.h"#include using namespace std;class B0//基类B0声明{public:void display(){cout<};class B1: public B0//公有派生类B1声明{public:void display...
  • 求高手解答解决方案5java允许多重继承,所以java没有虚继承的概念只有在虚继承的情况下,才把父类叫虚基类,实际上父类仍然是个普通父类,真正做出实现的是继承类抽象基类只是允许实例化,就像你把一个普通类的...
  • 1、Python不同版本的类Python2.2...Python2 中全部都是新式诶新式类都是继承自object的,新式类可以使用superPython2.2新式类:等同 python3,旧式类:能使用super,只能使用 类名. 调用父类的方法属性, 没有base...
  • abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性,甚至可以相互替换,因此很开发者在进行抽象类定义时对于abstract class和interface的选择显得比较随意。其实,两者之间还是有很大的.....
  • 虚继承与空基类优化

    2021-04-04 15:00:40
    参考文章 虚继承与空基类优化 。 class TEST_X {}; // 1 class TEST_Y : public virtual TEST_X { // 12 virtual void fun() {}; // 表4 //指虚基类的表指针4 char c; }; // 字节对齐 4 // 空基类...
  • _(self, y): SuperClass.__init__(self, 'x') # don't do this self.y = y 一些东西__new__ 还有另一种初始化实例的方法——这是Python中可变类型的子类的唯一方法。因此,如果要将str或tuple或另一个可变对象...
  • Calendar c = new Calendar(); 出现 new Calendar()就会执行...1、抽象类可以包括抽象方法,它反正不会去实例化,抽象类能被实例化,也就是能用new关键字去产生对象,抽象方法只需声明,而需实现,抽象类的...
  • 深入解析C++中派生类的构造函数基类的构造函数能被继承,在声明派生类时,对继承过来的成员变量的初始工作也要由派生类的构造函数来完成。所以在设计派生类的构造函数时,不仅要考虑派生类新增的成员变量,还要...
  • 为什么有了析构函数,就能先调用子类的析构函数? class A { virtual ~A(){} }; class B : A { virtual ~B(){} }; A *p = new B(); delete p; 唯一差别是,每个析构函数结束时会自动(隐含地)调上父类的析...
  • 具体如下:定义抽象类,需要使用abc模块,该模块定义了一个元类(ABCMeata),和装饰器 @abstractmethod, @abstractproperty如果要实例化继承了Foo 的子类,子类必须实现了Foo所有的抽象方法(跟java一样),否则实例化...
  • 最近就有想写几篇关于继承虚函数的笔记,所以回家到之后就奋笔疾书的写出来发布了应用sizeof函数求类巨细这个问题在很面试,口试题中很轻易考,而涉及到类的时候,又不得说类的继承虚继承函数,所以涉及到...
  • 虚继承中,虚基类是由最终的派生类初始的,换句话说,最终派生类的构造函数必须要调用虚基类的构造函数。对最终的派生类来说,虚基类是间接基类,而不是直接基类。这跟普通继承不同,在普通继承中,派生类构造...
  • 所谓的单继承指的是子类只继承一个基类,但实际应用中有可能需要继承多个类的情况,即多继承。联想一下现实生活,一个孩子会继承父亲的某些特征,同时也会继承母亲的特征,多继承的概念便难理解了。接下来的部分...
  • 本文实例讲述了Python面向对象类的继承。分享给大家供大家参考,具体如下:一、概述面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来...
  •  函数的调用是通过函数表来查找的,而函数表由类的实例化对象的vptr指针(vptr可以参考C++的函数表指针vptr)指向,该指针存放在对象的内部空间中,需要调用构造函数完成初始化。如果构造函数是函数,那么...
  • 基类中将一个函数声明为函数,使该函数具有属性,那么其所有派生函数中该函数的重写都具备了属性,也就使得基类指针可以调用派生类实例继承自该基类的所有成员函数,且若有重写,调用的都是重写后的函数。...
  • 1.没有继承的情况单独一个类的场景下,初始顺序为依次为 静态变量和静态代码块(看两者的书写顺序),继承基类的构造函数,成员变量,被调用的构造函数。代码呈现:public class Test {public static void main...
  • c++虚继承多继承

    2021-02-28 18:56:35
    指针,函数剖析 例1: 以下代码输出什么? #include <iostream> using namespace std; class A { protected: int m_data; public: A(int data = 0) {m_data=data;} int GetData() { return doGetData...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 155,366
精华内容 62,146
关键字:

多继承的虚基类不能够实例化