-
2018-02-01 12:05:44
虚继承是多重继承特有的概念,这里需要明确的是,虚继承与虚函数继承是完全不同的概念。
虚继承是为解决多重继承而出现的,可以节省内存空间
举例:
类c4继承自类c2和类c3,类c2继承自类c1,类c3页继承自类c1。这样类c1就出现2次,我们可以通过虚继承节省内存空间,如下所示
操作方法:将类c2和类c3对类c1的继承定义为虚拟继承。
c++代码如下
class c1 {}; class c2:public virtual c1; {}; class c3:public virtual c1; {}; class c4:public c2,public c3 {};
更多相关内容 -
C++普通继承和虚继承详解
2020-06-07 22:18:46所谓的继承就是一个类继承了另一个类的属性和方法,这个新的类包含了上一个类的属性和方法,被称为子类或者派生类,被继承的类称为父类或者基类。 继承特点 子类拥有父类的所有属性和方法(除了构造函数和析构函数...继承
继承概念
所谓的继承就是一个类继承了另一个类的属性和方法,这个新的类包含了上一个类的属性和方法,被称为子类或者派生类,被继承的类称为父类或者基类。
继承特点
- 子类拥有父类的所有属性和方法(除了构造函数和析构函数)。
- 子类可以拥有父类没有的属性和方法。
- 子类是一种特殊的父类,可以用子类来代替父类。
- 子类对象可以当做父类对象使用。
继承格式
class 派生类类名: 继承方式 基类名
{
成员变量和成员函数的声明
}
继承方式: public protected private不同继承方式对子类访问的影响
总结- 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。
- 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
- 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public >protected > private。
- 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
- 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。
- 友元关系不能继承,也就是说基类友元不能访问子类的私有和保护变量。
基类和派生类对象赋值转换
- 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。
- 基类对象不能赋值给派生类对象。(可以理解为父类的结构小于子类,子类能切割和父类相同大小的结构给父类赋值,而父类不能满足子类的大小所以无法给子类赋值)
- 基类的指针可以通过强制类型转换赋值给派生类的指针。但是必须是基类的指针是指向派生类对象时才是安全的。
class Father { protected : int _age; // 年龄 }; class Son : public Father { public : int _Nu ; // 学号 }; void Test () { Son s1 ; // 1.子类对象可以赋值给父类对象/指针/引用 Father f1 = s1; Father *f2 = &s1; Father& f3 = s1; //2.基类对象不能赋值给派生类对象 s1 = f1; // 3.基类的指针可以通过强制类型转换赋值给派生类的指针 Father *f2 = &s1; Son* s2 = (Son*)f2; //把父类指针强转为子类指针 s2->_Nu = 10;
继承中的作用域
- 在继承体系中基类和派生类都有独立的作用域。
- 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 来进行显示访问)
- 成员函数的隐藏,子类只需要和父类函数名相同就能构成隐藏。
- 注意在实际中在继承体系里面最好不要定义同名的成员。
class Father { public: int _num = 100; }; class Son : public Father { public: void p() { cout << _name << " " << _num << endl; //这样只会输出子类的 _num,因为变量名相同子类会屏蔽父类 cout << _name << " " << Father::_num << endl; // 想要输处父类的 _num 需要再变量前加 基类::基类 } string _name = "Hi"; int _num = 10; }; int main() { Son s; s.p(); }
派生类的默认成员函数
- 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。(如果我们想构建一个不能被继承的类就可以把父类的构造函数私有化)
- 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
- 派生类的operator=必须要调用基类的operator=完成基类的复制。
- 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
- 派生类对象初始化先调用基类构造再调派生类构造。
- 派生类对象析构清理先调用派生类析构再调基类的析构。
继承与静态成员
基类定义的 static 静态成员,则整个继承体系里面有且只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。
class Person { public: Person() { ++_count; } public: static int _count; // 统计个数。 }; int Person::_count = 0; class Student : public Person { protected: int _stuNum; }; class Graduate : public Student { protected: int _GrdNum; }; int main() { Student s1; Student s2; Student s3; Graduate s4; cout << " 人数 :" << Person::_count << endl; // 4 Student::_count = 0; cout << " 人数 :" << Person::_count << endl; // 0 return 0; }
菱形继承
- 单继承:一个子类只有一个直接父类时称这个继承关系为单继承
2.多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
3.菱形继承:菱形继承是多继承的一种特殊情况
4.菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。
解决方式:
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在 Student 和 Teacher 的继承 Person 时使用虚拟继承(继承方式前面加 virtual),即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
一、虚继承原理
- 虚继承用于解决多继承条件下的菱形继承问题(数据冗余、存在二义性)。
底层实现原理与编译器相关,一般通过虚基类指针和虚基类表实现,每个虚继承的子类都有一个虚基类指针(占用一个指针的存储空间,4字节)和虚基类表(也被称作虚基表,不占用类对象的存储空间)(需要强调的是,虚基类依旧会在子类里面存在拷贝,只是仅仅最多存在一份而已,并不是不在子类里面了);当虚继承的子类被当做父类继承时,虚基类指针也会被继承。 - 实际上,vbptr 指的是虚基类表指针(virtual base table pointer,也叫虚基表指针),该指针指向了一个虚基类表(virtual table),虚表中记录了虚基类与本类的偏移地址;通过偏移地址,这样就找到了虚基类成员,而虚继承也不用像普通多继承那样维持着公共基类(虚基类)的两份同样的拷贝,节省了存储空间。
二、虚基类的声明和语法形式:
- class 派生类名:virtual 继承方式 基类名
三、虚基类的使用要点:
- 一个类可以在一个类族中用作虚基类,也可以用作非虚基类。
- 在派生类的对象中,同名的虚基类只产生一个虚基类子对象,而某个非虚基类产生各自的对象。
- 虚基类子对象是由最派生类(最后派生出来的类)的构造函数通过调用虚基类的构造函数进行初始化 (最派生类会先去调用虚基类的构造函数)。
- 最派生类是指在继承类结构中建立对象时所指定的类。
- 在派生类的构造函数的成员初始化列表中,必须列出对虚基类构造函数的调用,如果没有列出,则表示使用该虚基类的缺省构造函数。
- 在虚基类直接或间接派生的派生类中的构造函数的成员初始化列表中,都要列出对虚基类构造函数的调用。但只有用于建立对象的最派生类的构造函数调用虚基类的构造函数,而该派生类的所有基类中列出的对虚基类构造函数的调用在执行中被忽略,从而保证对虚基类子对象只初始化一次。
- 在一个成员初始化列表中,同时出现对虚基类和非虚基类构造函数的调用时,基类的构造函数先于非虚基类的构造函数执行。
- 虚基类并不是在声明基类时声明的,而是在声明派生类是,指定继承方式时声明的。因为一个基类可以在生成一个派生类作为虚基类,而在生成另一个派生类时不作为虚基类。
四、虚基表指针的存放位置
- 虚基表指针是存放在数据段的 - 虚基表指针是放在对象的开头的
虚继承的使用例子
// 不使用虚继承 class A { public: A(string s1) { cout << s1 << endl; } }; class B : public A { public: B(string s1, string s2) :A(s1) { cout << s2 << endl; } }; class C : public A { public: C(string s1, string s3) :A(s1) { cout << s3 << endl; } }; class D : public B, public C { public: D(string s1, string s2, string s3, string s4) //这里就和继承顺序有关系了 : C(s1, s3), B(s1, s2) //D 是普通继承,所以D中即存在B也存在C,所以D在构造时会根据继承顺序先去调B的构造,而B会在构造自身时先去调用A的构造,C也一样,所以输出顺序为 A、B、A、C、D { cout << s4 << endl; } }; int main() { D d("A", "B", "C", "D"); return 0; } ************************************************** //使用虚继承 class A //此时 A 类也被称作 虚基类 { public: A(string s1) :_s1(s1) { cout << s1 << endl; } string _s1; }; class B :virtual public A //B使用了虚继承,B中包含 vbptr(A的虚基表指针)、_s1、_s2 { public: B(string s1, string s2) :A(s1), _s2(s2) { cout << s2 << endl; } string _s2; }; class C : virtual public A //C使用了虚继承,C中包含 vbptr(A的虚基表指针)、_s1、_s3 { public: C(string s1, string s3) :A(s1), _s3(s3) { cout << s3 << endl; } string _s3; }; class D : public B,public C //D是普通继承,所以D中包含了一个B(vptr(A的虚基表指针)、_s2)、C(vptr(A的虚基表指针)、_s3)、_s1 和 _s4, { public: D(string s1, string s2, string s3, string s4) //在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员并且虚基类子对象是由最派生类(最后派生出来的类)的构造函数通过调用虚基类的构造函数进行初始化 //所以这里会先根据D中的 A(s1)去构造D中的A类对象_s1,然后会再次根据继承的顺序依次去构造B和C,因为D中的_s1独有一份(A输出什么只跟A(s1)中传入的s1相关和B、C中的第一参无关),所以B、C中不会再次去构建_s1,最终输出顺序为 A、B、C、D :C(s4, s3), A(s1), B(s3, s2), _s4(s4) { cout << s4 << endl; } string _s4; }; int main() { D d("A", "B", "C", "D"); return 0; } ************************************************** //不使用虚继承派生类结构体的大小 class A { protected: int _d; }; class B : public A { protected: int _d1; }; class C : public A { protected: int _d2; }; class D : public B, public C { protected: int _d3; }; int main() { D c; cout << sizeof(c) << endl; // 20,因为没使用虚继承,B、C中除了自己原有的成员变量之外还各自继承了A中的成员变量,D继承了B、C后,除了自身的成员变量外还继承了B、C的成员变量,所以它里面有 _d1,_d2,_d1,_d3,_d4 } ************************************************* class A { protected: int _d; }; class B :virtual public A // _d _d1 vbptr { protected: int _d1; }; class C : virtual public A // _d _d2 vbptr { protected: int _d2; }; class D : public B, public C // _d (_d1 vbptr) (_d2 vbptr) _d3 { protected: int _d3; }; int main() { D d; // 可以看出 d 中除了 4个 int 还有两个 vbptr(虚基表指针,一个4字节) 所以共 24个字节 cout << sizeof(d) << endl; //24 }
继承和组合
- public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
- 组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
- 优先使用对象组合,而不是类继承 。(因为继承中一个基类的改变会影响派生类的改变(破坏了类的封装),而组合却不会,组合容错率更好)
- 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高。
- 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。 组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。
- 实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系既可以用继承也可以用组合的话,就用组合。
-
C++虚继承与普通继承的区别
2015-04-16 15:54:17虚继承的时候在子类的对象中会多出一个叫虚类指针的大小,有的资料说这个指针指向的内存里面包含了该子类的偏移量和到基类的距离。但是我跟踪过这段内存,发现里面的数据没有规律,也找不到更多的支撑材料,权且先...虚继承的时候在子类的对象中会多出一个叫虚类指针的大小,有的资料说这个指针指向的内存里面包含了该子类的偏移量和到基类的距离。但是我跟踪过这段内存,发现里面的数据没有规律,也找不到更多的支撑材料,权且先知道子类的对象里面会有这么一个东西吧。
先总结虚拟继承中比较特殊的地方,希望能够对大家有所帮助:
虚继承时子类的虚函数不再是添加到父类部分的虚表中,而在普通的继承中确实直接添加到父类的虚表中,这就意味着如果虚继承中子类父类都有各自的虚函数,在子类里面就会多出一个虚表指针,而普通的继承却不会这样。代码说明:
class B
{
public:
char b[3];
public:
virtual void bb()
{
cout<<"B"<<endl;
}
};class C:public virtual B
{
public:
char c[3];
public:
virtual void cc()
{
cout<<"C"<<endl;
}
};int main()
{
C c;c.c[0]=1;
c.c[1]=2;
c.c[3]=3;c.b[0]=4;
c.b[1]=5;
c.b[2]=6;C*ptr=&c;
void (*PtrFun)();
int *p,*p2;memcpy(&p,&ptr,4);//使得p指向c的地址
memcpy(&p2,p,4); //使得p2指向虚表的地址
memcpy(&PtrFun,p2,4);
PtrFun();//调用C的虚函数表明前四个字节是C的虚函数表的位置p+=1;
//跳过C的虚类指针
p+=1;
//跳过C的数组地址
p+=1;memcpy(&p2,p,4);//定位到B的虚表地址
memcpy(&PtrFun,p2,4); //调用B的虚函数
PtrFun();cout<<sizeof(c)<<endl;
}
普通继承中子类中则不会多出虚类指针,子类也不会有自己单独的虚函数列表,子类的虚函数会被嵌到基类部分的虚函数表的后面,代码如下:
int main()
{
C c;c.c[0]=1;
c.c[1]=2;
c.c[3]=3;c.b[0]=4;
c.b[1]=5;
c.b[2]=6;C*ptr=&c;
void (*PtrFun)();
int *p,*p2;memcpy(&p,&ptr,4);//使得p指向c的地址
memcpy(&p2,p,4); //使得p2指向虚表的地址
memcpy(&PtrFun,p2,4);
PtrFun();//调用C的虚函数表明前四个字节是C的虚函数表的位置p2+=1;
memcpy(&PtrFun,p2,4);
PtrFun();cout<<sizeof(c)<<endl;
}
从中我们不难发现虚拟继承父类子类的内存排列方式也有很大的差别,普通继承中父类在前,子类在后;虚拟继承中先是子类部分的内存,接着再是父类的内存。对于虚拟继承中的多次继承就更奇葩了。
附上代码,大家应该很容易看明白其中的内存是如何分布的:
#include <iostream>
using namespace std;class A
{
public:
char a[3];
public:
virtual void ba()
{
cout<<"A"<<endl;
}
};class B:public virtual A
{
public:
char b[3];
public:
virtual void bb()
{
cout<<"B"<<endl;
}
};class C:public virtual B
{
public:
char c[3];
public:
virtual void cc()
{
cout<<"C"<<endl;
}
};int main()
{
C c;
C*ptr=&c;
void (*PtrFun)();
int *p,*p2;memcpy(&p,&ptr,4);//使得p指向c的地址
memcpy(&p2,p,4); //使得p2指向虚表的地址
memcpy(&PtrFun,p2,4);
PtrFun();//调用C的虚函数表明前四个字节是C的虚函数表的位置p+=1;
//跳过C的虚类指针
p+=1;
//跳过C的数组地址
p+=1;memcpy(&p2,p,4);//我觉得应该是到B的地址了,但是确实到A的地址
memcpy(&PtrFun,p2,4); //大概是将A放到了前面吧
PtrFun();//调用A的虚函数,表明此时的p所保存的是A的虚函数表的位置p+=1;
//跳过A的数组地址
p+=1;memcpy(&p2,p,4);
memcpy(&PtrFun,p2,4);
PtrFun();//调用B的虚函数,表明此时的p所保存的是B的虚表地址c.c[0]=1;
c.c[1]=2;
c.c[3]=3;c.b[0]=4;
c.b[1]=5;
c.b[2]=6;c.a[0]=7;
c.a[1]=8;
c.a[2]=9;
}
没错 C的内存是在前面,但是后面紧接着的不是B的内存而是C的内存,至于为什么会这样分配,这个我也不知道,希望知道的人可以指点一下。(普通继承下是 C B A)
-
C++ 虚继承对基类构造函数调用顺序的影响
2021-01-20 03:48:10而继承包含了虚拟继承和普通继承,在可见性上分为public、protected、private。可见性继承比较简单,而虚拟继承对学习c++的难度较大。 首先,虚拟继承与普通继承的区别有: 假设derived 继承自base类,那么... -
什么是虚继承,为什么要虚继承,虚继承和普通继承有什么区别
2017-10-05 13:23:50类D继承自类B和类C,而B类和C类都继承自类A,类D中会两次继承A,为了节省空间,可以将B、C对A对A的继承定义为虚拟继承,而A就成了虚拟基类 代码演示: class A; class B :public virtual A; class C :public virtual ...
代码或讲解有任何问题,请在评论区告知,不剩感激!!!!!!
类D继承自类B和类C,而B类和C类都继承自类A,类D中会两次继承A,为了节省空间,可以将B、C对A对A的继承定义为虚拟继承,而A就成了虚拟基类
代码演示:
class A;
class B :public virtual A;
class C :public virtual A;
class D :public B,public C;
-
【C++】菱形继承和虚继承
2022-03-05 12:55:37文章目录一、菱形继承二、虚继承 一、菱形继承 二、虚继承 -
C++普通继承、多继承、虚继承内存空间排布分析
2022-01-19 12:33:17C++普通继承、多继承、虚继承内存空间排布分析一、普通继承二、多继承三、菱形继承四、虚继承1.部分虚继承2.完全虚继承总结 一、普通继承 代码 class A { public: virtual void virtualfunA(){ } virtual void ... -
为什么需要虚继承,虚继承的实现原理
2019-05-12 21:07:54虚继承和虚函数是完全无相关的两个概念。 虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:其一,浪费存储空间;第二,存在二义性问题,通常可以... -
C++中虚继承
2021-01-06 09:25:14一、虚继承和虚基类 1、多继承产生的冲突 在C++中多继承时很容易产生命名冲突,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承,如下图所示... -
虚函数继承与虚继承
2019-09-03 11:09:35虚函数继承和虚继承是完全不同的两个概念。 虚函数继承是解决多态性的,当用基类指针指向派生类对象的时候,基类指针调用虚函数的时候会自动调用派生类的虚函数,这就是多态性,也叫动态编联。 虚继承就是为了节约... -
c++ 虚继承与继承的差异
2016-03-25 17:30:33c++ 虚继承与继承的差异 2012-09-28 20:51 8313人阅读 评论(2) 收藏 举报 ...前面一篇文章,说明了在C++ ...经过仔细推敲,发现没有彻底说清楚虚继承与普通继承之间的关系。所以用下面的文字再说明一下。 首 -
虚函数的三种继承(普通继承,虚函数继承,虚继承)
2020-05-16 20:58:37#include using ... C类,虚函数继承,一个整理4B,自己的虚指针4B(指向的虚函数表仅cc一个函数),然后把B类的12B全继承一份,共20B 注意:上面两个继承与虚继承是不一样的,虚继承就记住是菱形继承时用到即可 -
C++之虚继承和虚基类
2021-07-23 16:20:26C++之虚继承和虚基类虚继承 虚继承 C++中使用多重继承存在一些缺点,比如继承那章提到的命名冲突,就是典型的使用多重继承带来的混乱,下列程序也是,若A中存在成员变量a,A的派生类B和C都继承了A中protected修饰的... -
虚继承和普通继承的一些不同(持续更新)
2013-08-19 20:25:54普通继承中,子类和基类中至少任何一方有虚函数,那么那个虚指针就是双方公用的,通过sizeof类的大小可以看出; 虚继承中,子类和基类的虚函数指针是不同的,没有任何关系,子类中有一个指向基类的指针。 -
c++中菱形继承中出现的问题和虚继承虚基类使用
2021-09-15 09:34:18目录普通继承关系的菱形继承如下虚基类虚继承利用虚继承解决,保存多份不同成员变量的问题 普通继承关系的菱形继承如下 基类Person被public继承以后会生成2份成员变量,再给孙子类的会保存,代码如下,通过代码能看到,... -
虚继承与普通继承之间的关系
2014-02-28 22:10:49首先,重复一下虚拟继承与普通继承的区别有: 假设derived 继承自base类,那么derived与base是一种“is a”的关系,即derived类是base类,而反之错误; 假设derived 虚继承自base类,那么derivd与base是一种... -
C++虚继承
2021-07-04 16:15:54虚继承是由多继承和多重继承引发的一些问题。先说明虚继承和虚函数是两个不同的概念。下面看一个例子: 1、菱形继承 两个派生类继承同一个基类,又有某个类同时继承者两个派生类,这种继承被称为菱形继承或者钻石... -
c++对象模型05:虚继承内存布局
2021-08-07 16:23:43虚继承的派生类的内存布局与普通继承很多不同,主要体现在: 虚继承的派生类,如果定义了新的虚函数,则编译器为其生成一个虚函数指针(vptr)以及一张虚函数表。该vptr位于对象内存最前面。(非虚继承时,派生类新... -
虚继承基本原理
2022-02-16 10:03:56二、从内存布局看虚继承原理 1、普通类的菱形继承:虚基类的成员会被拷贝两份,一模一样的,导致了空间的浪费; class A { public: int age=10; }; class B: public A { public: int b; }; class C : public A { ... -
C++多重继承分析——《虚继承实现原理(虚继承和虚函数)》
2019-09-29 07:43:01一、虚继承和虚函数概念区分 虚继承和虚函数是完全无相关的两个概念。 虚继承是解决C++多重继承问题的一种手段,从不同途径继承来的同一基类,会在子类中存在多份拷贝。这将存在两个问题:其一,浪费存储空间;第... -
C++虚继承详解
2022-04-21 15:57:14C++虚继承详解 看侯捷老师的C++内存模型时,讲到了虚继承。虚继承算是C++特有的知识了,特此记录下。 什么是虚继承 由于C++支持多继承,可能会出现菱形继承,代码如下: #include <iostream> // std::cout ... -
【C++】继承和派生、虚继承和虚基类、虚基类表和虚基类指针
2020-07-23 14:15:54继承和派生、虚继承和虚基类、虚基类表和虚基类指针继承和派生继承概述继承基本概念派生类中的成员继承的内容派生类定义派生类访问控制对象构造和析构对象构造和析构的调用顺序继承中的构造和析构的调用规则调用子类... -
C++虚函数继承与虚继承
2018-02-22 15:56:40虚函数继承和虚继承是完全不同的两个概念。 虚函数继承是解决多态性的,当用基类指针指向派生类对象的时候,基类指针调用虚函数的时候会自动调用派生类的虚函数,这就是多态性,也叫动态编联。 虚继承就是为了节约... -
C++ 虚函数和虚继承解析
2018-08-09 21:53:58本文针对C++里的虚函数,虚继承表现和原理进行一些简单分析,有不对的地方请指出。下面都是以VC2008编译器对这两种机制内部实现为例。 有喜欢或者想学习C/C++的朋友加一下我的C/C++交流群815393895。谢谢大家的支持... -
C++的继承和派生(三)虚继承和虚基类、虚继承时的构造函数
2020-06-25 15:34:10虚继承和虚基类的概念 多继承 多继承是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的所有的成员,但是多继承很容易造成命名冲突的问题,例如典型的菱形继承 如上图所示,假设当类A中有一... -
C++中虚函数与虚继承区别浅析笔记
2019-06-23 09:19:50虚继承和虚函数是完全无相关的两个概念。 虚函数:是在函数声明/定义时,必须加上virtual关键字。作用就是让其派生类能够覆盖此函数,从而实现多态(运行时多态)。 补充:编译时多态性:通过重载函数和运算符重载实现... -
C++虚继承与虚函数
2017-10-03 21:11:34虚继承在菱形继承中出现的数据二义性问题,使得数据访问时变得复杂,并且导致了数据冗存。虚继承则解决了从不同途径继承来的同名的数据成员在内存中有不同的拷贝造成数据不一致问题。关键字:virtual用法:将共同... -
虚函数&虚继承
2019-09-26 22:52:21多继承和虚继承多重继承虚继承,解决二义性,节省内存占用 多重继承 虚继承,解决二义性,节省内存占用 -
C++虚继承时的构造函数
2021-05-24 10:05:06在虚继承中,虚基类是由最终的派生类初始化的,换句话说,最终派生类的构造函数必须要调用虚基类的构造函数。对最终的派生类来说,虚基类是间接基类,而不是直接基类。这跟普通继承不同,在普通继承中,派生类构造...