虚继承 订阅
虚继承 是面向对象编程中的一种技术,是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类型直接或间接派生的其它类。虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如下图所示。类D继承自类B1、B2,而类B1、B2都继承自类A,因此出现如右图所示的局面(非虚基类)。 展开全文
虚继承 是面向对象编程中的一种技术,是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类型直接或间接派生的其它类。虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如下图所示。类D继承自类B1、B2,而类B1、B2都继承自类A,因此出现如右图所示的局面(非虚基类)。
信息
属    于
面向对象编程中的一种技术
包    含
多重继承中特有的概念
含    义
指定的基类,在继承体系结构中
中文名
虚继承
虚继承基本概述
虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。如下图所示。类D继承自类B1、B2,而类B1、B2都继承自类A,因此出现如右图所示的局面(非虚基类)。为了节省内存空间,可以将B1、B2对A的继承定义为虚拟继承,而A就成了虚拟基类。最后形成如左图所示的情况。实现的代码如下: class A;class B1:public virtual A;class B2:public virtual A;class D:public B1,public B2;
收起全文
精华内容
下载资源
问答
  • C++的三大特性为:封装,继承,多态。但是在继承中,存在一些使用方面的问题需要注意,下面这篇文章主要给大家总结介绍了关于C++中菱形继承和虚继承的问题,需要的朋友可以参考借鉴,下面来一起看看吧。
  • 虚继承

    千次阅读 2019-08-02 10:19:33
    虚继承:主要解决内存重复的问题,同时避免访问冲突。 声明格式:class 类名 :virtual 继承方式 类名 继承方式可以缺省,缺省之后默认继承方式为 private 私有继承。 #include<iostream> #include<...

     

    虚继承:主要解决内存重复的问题,同时避免访问冲突。

    声明格式:class 类名 :virtual 继承方式  类名

    继承方式可以缺省,缺省之后默认继承方式为 private 私有继承。

    #include<iostream>
    #include<string>
    using namespace std;
    class A
    {
    public:
    	A(int a) :ma(a){}
    public:
    	int ma;
    };
    class B :virtual public A
    {
    public:
    	B(int b) :mb(b), A(b){}
    public:
    	int mb;
    };
    class C :virtual public A
    {
    public:
    	C(int c) :mc(c), A(c){}
    public:
    	int mc;
    };
    
    class D 
    {
    public:
    	D(int d) : md(d){}
    public: 
    	int md;
    };
    int main()
    {
    	D d(10);
    	return 0;
    }

    上述代码的继承关系和内存布局:

     普通继承:普通继承会将A,继承多次,造成内存重复,有可能产生访问冲突。

    虚继承:当我们在继承方式前加virtual 关键字,进行虚继承,虚继承内存中会通过虚基类指针指向虚基类表,该表中的数据是为虚基类指针到基类的存储区域的偏移值。

    虚继承的内存布局:非虚基类排列 > 虚基类排列

    虚基类的构造顺序:虚基类的构造函数  > 非虚基类的构造函数

    虚基类指针的合并:

    1、平行层次相同作用域下的vbptr合并。

    2、非平行层次的vbptr向第一个vbptr合并。

    展开全文
  • 本文以实例形式较为全面的讲述了C++的多重继承与虚继承,是大家深入学习C++面向对象程序设计所必须要掌握的知识点,具体内容如下: 一、多重继承 我们知道,在单继承中,派生类的对象中包含了基类部分 和 派生类...
  • 该资源为虚函数和虚继承及其结合的内存布局的测试,文中写明了有详细测试结果。
  • c++基础语法:虚继承

    2020-12-15 17:26:26
    虚继承 的概念的提出主要是为了解决C++多继承的问题,举个最简单的例子: 代码如下:class animal{ public : void op() {cout << “hello animal” ;} };class tiger : public animal { public : void tg...
  • 继承作为面向对象编程的一种基本特征,其使用频率...  假设derived 虚继承自base类,那么derivd与base是一种“has a”的关系,即derived类有一个指向base类的vptr。(貌似有些牵强!某些编译器确实如此)  因此虚
  • 这篇文章主要讲解G++编译器中虚继承的对象内存分布问题,从中也引出了dynamic_cast和static_cast本质区别、虚函数表的格式等一些大部分C++程序员都似是而非的概念。本文是介绍C++的技术文章,假定读者对于C++有比较...
  •  GCC中, 无论是虚函数还是虚继承, 都需要将指针存储在虚函数表(virtual function table), 占用4个字节.  继承会继承基类的数据, 和虚函数表, 即继承基类的空间.  代码: /* * test.cpp * * Created ...
  • 一、多继承 C++语言支持多继承,一个子类可以有多个父类,子类拥有所有父类的成员变量,子类继承所有父类的成员函数,子类对象可以当作任意父类对象使用。 class Derived : public ...二、虚继承 多继承虽然很不错

    一、多继承

    C++语言支持多继承,一个子类可以有多个父类,子类拥有所有父类的成员变量,子类继承所有父类的成员函数,子类对象可以当作任意父类对象使用。

    class Derived : public BaseA,
                       public BaseB,
                       public BaseC   
    {
    
    };

    当两个父类存在相同名称的成员函数,在子类中,可以通过【类名::成员函数名称】的方式来确认调用的是哪个父类的函数。

    二、虚继承

    多继承虽然很不错,但是有个致命的菱形继承问题,例如:

    若此时在调用一个Doctor对象中,调用一个从People继承而来的方法时,将会编译错误,提示二义性。

    #include <iostream>  
    
    using namespace std;
    
    class People {
    public:
        void People_test() {
            cout << "People_test" << endl;
        }
    
    };
    class Teacher : public People {
    public:
    
    };
    class Student : public People {
    public:
    
    };
    class Doctor :public Teacher, public Student {
    public:
    
    };
    
    int main()
    {
        Doctor d;
        d.People_test();
        return 0;
    }

    执行结果如下:

    此时就需要一个虚继承,C++提供了虚基类和虚继承机制,实现了在多继承中只保留一份共同成员,完美解决了菱形多继承导致的成员冗余问题。
    虚继承中,中间层父类不再关注顶层父类的初始化,最终子类必须直接调用顶层父类的构造函数。
    虚继承的语法如下:
    class 派生类名:virtual 继承方式 基类名

    现在只需要在上面Teacher以及Student继承People的前面加上virtual关键字声明为虚继承,则可以解决上述二义性问题。

    到目前为止,我们调用的都是各个类的默认构造函数,但是如果此时,我们想要调用其带参参数时,会如何呢,代码如下:

    #include <iostream>  
    
    using namespace std;
    
    class People {
    public:
        void People_test() {
            cout << "People_test" << endl;
        }
        People(int a) {
            cout << "people have one param " << a << endl;
        }
        People() {
            cout << "people default" << endl;
        }
    };
    class Teacher :virtual public People {
    public:
        Teacher(int a, int b) : People(a) {
            
        }
    };
    class Student :virtual public People {
    public:
        Student(int a, int b) : People(a) {
            
        }
    };
    class Doctor :public Teacher, public Student {
    public:
        Doctor(int a, int b) : Teacher(a, b), Student(a, b) {
         
        }
    };
    
    int main()
    {
        Doctor d(1,2);
        
        return 0;
    }

    此时的运行结果是什么呢?此时的运行结果是:

    可能你会问,明明在Teacher和Student类的初始化列表里面调用了People的带参构造函数,为什么没有得到调用呢,因为C++语言规定,在基类是虚的时候,将禁止信息通过中间类自动传递给基类,因此上述Teacher和Student类构造函数不会调用People的带参构造函数,而只会调用一次默认构造函数,但如果你不希望使用默认构造函数来构造虚基类对象,可以在子类中使用这样的初始化列表:

    Doctor(int a, int b) :People(b), Teacher(a, b), Student(a, b) {
         
        }

    此时的运行结果为:

    结论:如果类有虚基类,则除非只需使用该虚基类的默认构造函数,否则必须显式地调用该虚基类的你一个构造函数。

    三、虚基类和支配

    我们知道,使用非虚基类时,如果子类从不同父类那里继承来了同名成员,如果我们调用时候不用类名进行限定,将导致二义性。但是如果此时是虚基类,且调用时候不加类名进行限定,也可能不会导致二义性,因为在某些情况下,如果某个类的成员优先于其他类的成员,则使用它时,也不会导致二义性。那优先级如何判断呢?派生类中的名称优先于直接或间接祖先类的相同名称。

    class People {
    public:
        void test() {
            cout << "People test" << endl;
        }
        People(int a) {
            cout << "people have one param " << a << endl;
        }
        People() {
            cout << "people default" << endl;
        }
    };
    class Teacher :virtual public People {
    public:
        Teacher(int a, int b) : People(a) {
            
        }
        void test() {
            cout << "Teacher test" << endl;
        }
    };
    class Student :virtual public People {
    public:
        Student(int a, int b) : People(a) {
            
        }
    };
    class Doctor :public Teacher, public Student {
    public:
        Doctor(int a, int b) :People(b), Teacher(a, b), Student(a, b) {
         
        }
    };
    
    int main()
    {
        Doctor d(1,2);
        d.test();
        return 0;
    }

    运行结果为:

    若此时在Student中在定义个test函数,将会导致二义性,因为Teacher和Student都不是对方的基类。

    另外,这个二义性和访问规则无关,即使Student中的test是私有的,但仍会导致二义性。

    展开全文
  • C++菱形继承问题和虚继承分析

    万次阅读 多人点赞 2019-01-10 00:03:58
    基类的作用是 在间接继承共同基类时只保留一份基类成员。 class A//A 基类 { ... }; //类B是类A的功用派生类, 类A是类B的基类 class B : virtual public A { ... }; //类C是类A的功用派生类, 类A是类C的基类...

    二义性

    在面向对象中,常常存在这样的事情,一个派生类它有两个或两个以上的基类,这种行为称作多重继承,示意图如下:

    在这里插入图片描述

    如果在多重继承中Class A 和Class B存在同名数据成员,则对Class C而言这个同名的数据成员容易产生二义性问题。这里的二义性是指无法直接通过变量名进行读取,需要通过域(::)成员运算符进行区分。例如:

    //基类A
    class A 
    {
    public:
        A() :m_data(1), m_a(1)
        {
        }
        ~A(){}
    
    public:
        int m_data;      //同名变量,类型无要求
        int m_a;
    };
    //基类B
    class B
    {
    public:
        B() :m_data(1), m_b(1)
        {
        }
        ~B(){}
    
    public:
        int m_data;      //同名变量,类型无要求
        int m_b;
    };
    
    class C  : public A, public B
    {
    
    };
    
    int _tmain(int argc,  _TCHAR* argv[])
    {
        C Data;
       //Data.m_data  = 10;   //错误, 提示指向不明确
       
       //通过域成员运算符才可以访问,使用不方便
        Data.A::m_data = 10.1; 
        Data.B::m_data = 20;
        
        std::cout << Data.A::m_data << "   " << Data.B::m_data << std::endl;
    
        return 0;
    }
    

    分析

    我们可以通过域(::)运算符对同名变量进行读写操作,但是不是很方便,不能直接通过.变量的形式进行操作,形如Data.m_data

    菱形继承

    在多重继承中,存在一个很特殊的继承方式,即菱形继承。比如一个类C通过继承类A和类B,但是类A和类B又同时继承于公共基类N。示意图如下:
    在这里插入图片描述

    这种继承方式也存在数据的二义性,这里的二义性是由于他们间接都有相同的基类导致的。 这种菱形继承除了带来二义性之外,还会浪费内存空间

    代码如下:

    //公共基类
    class N
    {
    public:
        N(int data1, int data2, int data3) : 
            m_data1(data1), 
            m_data2(data2), 
            m_data3(data3)
        {
            std::cout << "call common constructor" << std::endl;
        }
        virtual ~N(){}
    
        void    display()
        {
            std::cout << m_data1 << std::endl;
        }
    
    public :
        int     m_data1;
        int     m_data2;
        int     m_data3;
    };
    
    
    class A : /*virtual*/ public N
    {
    public:
        A() :N(11, 12, 13), m_a(1)
        {
            std::cout << "call class A constructor" << std::endl;
        }
        ~A(){}
    
    public :
        int m_a;
    };
    
    class B :  /*virtual*/ public N
    {
    public:
        B() :N(21, 22, 23),m_b(2)
        {
            std::cout << "call class B constructor" << std::endl;
        }
        ~B(){}
    
    public :
        int m_b;
    };
    
    
    class C : public A ,  public B
    {
    public:
        //负责对基类的初始化
        C() : A(), B(),
            m_c(3)
        {
            std::cout << "call class C constructor" << std::endl;
        }
        void show()
        {
            std::cout << "m_c=" << m_c << std::endl;
        }
    
     public :
        int m_c;
    };
    

    我们通过vs自带的内存分析模型工具,得到如下的内存分布模型:

    在这里插入图片描述

    我们发现在类C中存在 两份的基类N,分别存在类A和类B中,如果数据多则严重浪费空间,也不利于维护, 我们引用基类N中的数据还需要通过域运算符进行区分。例如:

    C data;
    data.A::m_data1 = 10;
    data.B::m_data1 = 10;
    

    虚基类 & 虚基类

    为了解决上述菱形继承带来的问题,C++中引入了虚基类,其作用是 在间接继承共同基类时只保留一份基类成员,虚基类的声明如下:

    class A//A 基类
    { ... };
    
    //类B是类A的公用派生类, 类A是类B的虚基类
    class B : virtual public A
    {  ... };
    
    //类C是类A的公用派生类, 类A是类C的虚基类
    class C : virtual public A
    {  ... };
    

    虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式声明的。

    虚继承是声明类时的一种继承方式,在继承属性前面添加virtual关键字。

    //此时基类A并不是虚基类,因为声明继承类D时没有指定虚继承方式
    class D : public N
    {  
      ... 
    };
    

    在内存分析模型中没有virtual关键字。
    在这里插入图片描述

    虚基类的初始化

    这里直接说明结论,对于虚基类的初始化是由最后的派生类中负责初始化。

    在最后的派生类中不仅要对直接基类进行初始化,还要负责对虚基类初始化

    例如:

     // 类A和类B是虚继承方式
    class C : public A, public B
    {
    public:
        //负责对直接基类的初始化 以及虚基类的初始化
        C() : A(), B(), N(31,32,33) ,
            m_c(3)
        {
            std::cout << "call class C constructor" << std::endl;
        }
        void show()
        {
            std::cout << "m_c=" << m_c << std::endl;
        }
    
    public:
        int m_c;
    };
    

    虚基类构造次数

    C++编译系统只执行最后的派生类对基类的构造函数调用,而忽略其他派生类对虚基类的构造函数调用。从而避免对基类数据成员重复初始化。因此,虚基类只会构造一次。

    若不是普通的继承,则存在重复构造,左边普通继承,右边虚继承,看下图:
    在这里插入图片描述

    有虚基类的派生类中,虚基类只有一个构造函数输出"call common constructor

    虚基类的内存分析

    我们通过内存模型分析工具,得到最后派生类的内存模型对比图:

    右边是菱形继承的内存分析,左图是有虚基类的派生类C的内存分析:
    在这里插入图片描述

    我们发现虚继承的派生类C的内存比非虚继承的派生类C内存要小8个字节,并且虚基类在派生类中只存在一份,虚基类有virtual关键字标识

    因此我们在派生类中引用基类N的数据,不需要使用域成员运算符,因为只有一个基类对象。例如:

    C data;   
    data.m_data1 = 10;
    

    下面是对以上内容进行总结如下:
    在这里插入图片描述
    虚基类的完整分析代码如下:

    //公共基类
    class N
    {
    public:
        N(int data1, int data2, int data3) :
            m_data1(data1),
            m_data2(data2),
            m_data3(data3)
        {
            std::cout << "call common constructor" << std::endl;
        }
        virtual ~N(){}
    
        void    display()
        {
            std::cout << m_data1 << std::endl;
        }
    
    public:
        int     m_data1;
        int     m_data2;
        int     m_data3;
    };
    
    //虚继承方式
    class A : virtual public N
    {
    public:
        A() :N(11, 12, 13), m_a(1)
        {
            std::cout << "call class A constructor" << std::endl;
        }
        ~A(){}
    
    public:
        int m_a;
    };
    
    //虚继承方式
    class B :  virtual public N
    {
    public:
        B() :N(21, 22, 23), m_b(2)
        {
            std::cout << "call class B constructor" << std::endl;
        }
        ~B(){}
    
    public:
        int m_b;
    };
    
      // 类A和类B是虚继承方式
    class C : public A, public B
    {
    public:
        //负责对直接基类的初始化 以及虚基类的初始化
        C() : A(), B(), N(31,32,33), 
            m_c(3)
        {
            std::cout << "call class C constructor" << std::endl;
        }
        void show()
        {
            std::cout << "m_c=" << m_c << std::endl;
        }
    
    public:
        int m_c;
    };
    
    
    //此时基类N不是虚基类
    class D : public N
    {
    public:
        //负责对基类的初始化
        D() :N(41, 42, 43),
            m_d(4)
        {
            std::cout << "call class D constructor" << std::endl;
        }
        void show()
        {
            std::cout << "m_d=" << m_d << std::endl;
        }
    
    public:
        int m_d;
    };
    
    
    int _tmain(int argc,  _TCHAR* argv[])
    {
        C data;   
        //直接使用基类数据
        data.m_data1 = 10;
        
        return 0;
    }
    
    
    展开全文
  • 虚继承和虚函数是完全无相关的两个概念。 虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:其一,浪费存储空间;第二,存在二义性问题,通常可以...

    虚继承和虚函数是完全无相关的两个概念。

    虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:其一,浪费存储空间;第二,存在二义性问题,通常可以将派生类对象的地址赋值给基类对象,实现的具体方式是,将基类指针指向继承类(继承类有基类的拷贝)中的基类对象的地址,但是多重继承可能存在一个基类的多份拷贝,这就出现了二义性。

     

    虚继承可以解决多重继承前面提到的两个问题:

    虚继承底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。

     

    实际上,vbptr指的是虚基类表指针(virtual base table pointer),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。

     

    在这里我们可以对比虚函数的实现原理:他们有相似之处,都利用了虚指针(均占用类的存储空间)和虚表(均不占用类的存储空间)。

    虚基类依旧存在继承类中,只占用存储空间;虚函数不占用存储空间。

    虚基类表存储的是虚基类相对直接继承类的偏移;而虚函数表存储的是虚函数地址。

    补充:

    1、D继承了B,C也就继承了两个虚基类指针

    2、虚基类表存储的是,虚基类相对直接继承类的偏移(D并非是虚基类的直接继承类,B,C才是)

    #include<iostream>
    using namespace std;
     
    class A  //大小为4
    {
    public:
    	int a;
    };
    class B :virtual public A  //大小为12,变量a,b共8字节,虚基类表指针4
    {
    public:
    	int b;
    };
    class C :virtual public A //与B一样12
    {
    public:
    	int c;
    };
    class D :public B, public C //24,变量a,b,c,d共16,B的虚基类指针4,C的虚基类指针
    {
    public:
    	int d;
    };
     
    int main()
    {
    	A a;
    	B b;
    	C c;
    	D d;
    	cout << sizeof(a) << endl;
    	cout << sizeof(b) << endl;
    	cout << sizeof(c) << endl;
    	cout << sizeof(d) << endl;
    	system("pause");
    	return 0;
    }
    

     

    展开全文
  • 详解虚继承

    2012-12-21 09:58:09
    详解C++中虚继承虚函数 的要点和继承关系c++开发人员细细阅读哦
  • 继承和派生、虚继承和虚基类、虚基类表和虚基类指针继承和派生继承概述继承基本概念派生类中的成员继承的内容派生类定义派生类访问控制对象构造和析构对象构造和析构的调用顺序继承中的构造和析构的调用规则调用子类...
  • C++虚函数继承与虚继承

    万次阅读 2018-02-22 15:56:40
    虚函数继承和虚继承是完全不同的两个概念。 虚函数继承是解决多态性的,当用基类指针指向派生类对象的时候,基类指针调用虚函数的时候会自动调用派生类的虚函数,这就是多态性,也叫动态编联。 虚继承就是为了节约...
  • 虚继承是多重继承特有的概念,这里需要明确的是,虚继承与虚函数继承是完全不同的概念。 虚继承是为解决多重继承而出现的,可以节省内存空间 举例: 类c4继承自类c2和类c3,类c2继承自类c1,类c3页继承自类c1...
  • C++继承和多态(虚函数、纯虚函数、虚继承

    千次阅读 多人点赞 2019-03-05 11:32:40
    C++继承和多态(虚函数、纯虚函数、虚继承) 一:继承 继承的概念:为了代码的复用,保留基类的原始结构,并添加派生类的新成员。 继承的本质:代码复用 我们用下图解释下: 那么我们这里就可以提出几...
  • C++虚继承内存分布

    2019-06-01 13:57:20
    本周抽空看虚继承的时候,研究了下虚继承的对象的内存分布,C++虚继承主要解决了菱形继承访问不明确的问题。 上述继承关系,定义DD对象的构造函数输出如下:AA BB AA CC DD,对象中存在两份AA。若AA存在成员变量,...
  • 解析虚函数表和虚继承

    千次阅读 多人点赞 2016-10-31 11:48:56
    之前大二在学C++的时候一直对虚函数和虚继承有些晕(其实好像就是对virtual这个关键字不太熟悉)现在又学习到了一些,对虚函数表和虚继承的机制有了一点更深入的了解。 关于虚函数以及虚继承的基础知识,我自己也...
  • c++之虚继承(多重继承的问题)

    千次阅读 2020-06-13 13:56:11
    导引:多重继承和多继承 什么是多重继承:如图 什么是多继承:如图 继承中的特殊结构 菱形继承结构带来的问题,D会有两个A中的数据成员 class A { public: int a; }; class B:public A { public: int b; }; ...
  • C++ 虚继承实现原理(虚基类表指针与虚基类表)

    万次阅读 多人点赞 2018-06-03 18:45:20
    虚继承和虚函数是完全无相关的两个概念。虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:其一,浪费存储空间;第二,存在二义性问题,通常可以将...
  • c++中虚继承的作用

    千次阅读 2019-04-23 14:08:36
    在c++中虚继承的作用主要是在多重继承的问题上防止二义性的产生。 但是具体什么是虚继承呢? 就是在被继承的类前面加virtual关键字,这时候被继承的类就叫做虚基类。 具体请看下面的代码。 class Person; class Son1 ...
  • 虚继承和虚函数

    2019-06-06 15:41:40
    函数 在一个基类中声明一个函数为virtual类型,即virtual void Run();那么这个函数就是函数, 作用:实现多态,父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。 我这里是 ...
  • 虚继承和虚函数是完全无相关的两个概念。 虚函数:是在函数声明/定义时,必须加上virtual关键字。作用就是让其派生类能够覆盖此函数,从而实现多态(运行时多态)。 补充:编译时多态性:通过重载函数和运算符重载实现...
  • 虚继承详解及其内存分布

    千次阅读 2018-06-04 19:12:51
    什么是虚继承? 根据百度百科: 虚继承是面向对象编程中的一种技术,是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类型直接或间接派生的其它类。 虚拟继承是多重继承中特有的概念。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 147,236
精华内容 58,894
关键字:

虚继承

友情链接: xlmpilerframe.rar