帝国网站网站手机版怎么做,云浮东莞网站建设,佟年为韩商言做的网站,国外个人网站域名注册归纳编程学习的感悟#xff0c; 记录奋斗路上的点滴#xff0c; 希望能帮到一样刻苦的你#xff01; 如有不足欢迎指正#xff01; 共同学习交流#xff01; #x1f30e;欢迎各位→点赞 #x1f44d; 收藏⭐ 留言#x1f4dd; 苦难和幸福一样#xff0c;都是生命盛…
归纳编程学习的感悟 记录奋斗路上的点滴 希望能帮到一样刻苦的你 如有不足欢迎指正 共同学习交流 欢迎各位→点赞 收藏⭐ 留言 苦难和幸福一样都是生命盛开的花朵
一起加油 目录 一、前言
二、多继承的定义
三、多继承的构造函数以及调用顺序
例多继承下构造函数和析构函数的调用顺序 。
说明
四、多继承中的同名隐藏和二义性问题
⚡注意
例多继承同名隐藏示例。
说明 ?思考:
说明
例复杂版本的多继承同名隐藏示例。
五、虚基类
例虚基类应用示例。
⚡注意 六、总结
七、共勉 一、前言 根据派生类继承基类的个数将继承分为单继承和多继承。之前我们主要以单继承为例学习了派生类的定义以及使用中应注意的问题。多继承可以看成是单继承的组合它们有很多相似的特征。
二、多继承的定义 多继承的基类不只一个而是有多个派生类与每个基类之间的关系可以看作是一个单继承。多继承的定义格式如下:
class 派生类名:继承方式基类名 1,..,继承方式基类名 n { 派生类新定义的成员 }
三、多继承的构造函数以及调用顺序 在多继承方式下派生类构造函数要负责为每一个基类构造函数传入初始化的参数派生类的构造函数格式如下:
class 派生类名(总参数表):基类名 1(参数表 1),...,基类名 n(参数表 n) {
派生类数据成员的初始化 } 其中总参数表必须包含完成所有基类初始化所需的参数。 由于存在多个基类多继承规定派生类包含多个基类时构造函数的调用顺序是:先调用所有基类的构造函数再调用对象成员的构造函数(如果有对象成员 )最后调用派生类自己的构造函数。其中处于同一继承层次的各基类构造函数的调用顺序取决于定义派生类时所指定的基类的顺序与派生类构造函数中所定义的成员初始化列表顺序无关。如果类中有对象成员那么对象成员构造函数的调用顺序与对象在类中声明的顺序一致。
例多继承下构造函数和析构函数的调用顺序 。
#includeiostream
using namespace std;
class Base1
{public:Base1(int i){b1i;coutconstruct Base1endl;}void display(){coutb1b1endl;}~Base1(){coutdestruct Base1endl;}private:int b1;
};
class Base2
{public:Base2(int i){b2i;coutconstruct Base2endl;}void display(){coutb2b2endl;}~Base2(){coutdestruct Base2endl;}private:int b2;
};
class Derive:public Base2,public Base1
{public:Derive(int m):Base1(m2),Base2(m-2){dm;coutconstruct Deriveendl;}void display(){Base1::display();Base2::display();coutddendl;}~Derive(){coutdestruct Deriveendl;}private:int d;
};
int main()
{Derive d(10);d.display();return 0;
} 说明 构造函数的调用顺序是: Base2、Basel、Derive析构函数的调用顺序是: Derive、Base1、Base2。 四、多继承中的同名隐藏和二义性问题 上例中派生类中定义了与基类同名的函数 display对于在不同作用域声明的标识符可见性原则是:如果存在两个或多个包含关系的作用域外层声明了一个标识符而内层没有再次声明同名标识符那么外层标识符在内层依然可见;如果在内层声明了同名标识符则外层标识符在内层不可见此时称内层标识符隐藏了外层同名标识符这种现象被称为同名隐藏规则。 在类的派生层次结构中基类的成员和派生类新增加的成员都具有类作用域两者的作用范围不同是相互包含的两个层派生类在内层。这时在基类 Basel、Base2 中都定义了 display函数在派生类中也定义了 display 函数。如果在类外通过派生类对象 d 去调用 display 函数派生类新成员就会隐藏外层同名的成员直接使用成员名只能访问派生类的成员。若派生类中声明了与基类成员函数同名的新函数即使函数的形参表不同也不构成重载的关系从基类继承过来的同名函数也会被隐藏。若要访问被隐藏的成员就需要使用作用域操作符和基类名来限定。
⚡注意 当派生类中定义了与基类同名但具有不同的形参的函数时(形参个数不同或者形参类型不同)不属于函数重载这时派生类中的函数使基类中的函数隐藏调用父类中的函数必须使用父类名称来限定。只有在相同作用域中定义的函数才可以构成重载。 例多继承同名隐藏示例。
#includeiostream
using namespace std;
class Base1
{public:Base1(int i){b1i;coutconstruct Base1endl;}void display(){coutb1b1endl;}~Base1(){coutdestruct Base1endl;}private:int b1;
};
class Base2
{public:Base2(int i){b2i;coutconstruct Base2endl;}void display(){coutb2b2endl;}~Base2(){coutdestruct Base2endl;}private:int b2;
};
class Derive:public Base2,public Base1
{public:Derive(int m):Base1(m2),Base2(m-2){dm;coutconstruct Deriveendl;}void display(){coutddendl;}~Derive(){coutdestruct Deriveendl;}private:int d;
};
int main()
{Derive d(10);d.Base1::display();d.Base2::display();d.display();return 0;
}
说明 在主函数中、定义了派生类对象 d根据同名隐藏规则如果通过派生类对象访问display 函数只能访问派生类新添加的成员从基类继承过来的成员由于处于外层作用域而被隐藏。此时要通过d 访问从基类继承过来的成员就必须使用类名和作用域操作符;访问 Base1中的 display使用 Base1::display;访问 Base2 中的 display使用Base2::display。 通过作用域操作符明确且唯一地标识了派生类中由基类继承过来的成员解决了同名隐藏的问题。 ?思考: 如果在派生类中没有定义 display是不是就不存在同名隐藏那么通过d可以访问到的是 Base1的display还是 Base2的display?请改写程序验证你的想法。 假如我们把派生类定义的 display 函数删除,此时派生了继承了来自 Base1的 display 和 Base2的 display,由于 display 存在二义性,依然无法直接通过派生类对象d直接访问基类的成员 display。 如果某个派生类的部分或者直接基类是从另一个共同的基类派生而来在这些间接基类中从上一级基类继承来的成员拥有相同的名称在派生类中也会产生同名的现象。这种同名也需要通过作用域操作符来进行标识而且必须用直接基类来进行限定。
说明 基类 A 中声明了数据成员a、构造函数、析构函数和函数 fun0A 派生出了 B1和B2再以 B1、B2 作为基类共同派生出新类 C在派生类中都没有添加新的同名成员。这时的C类包含通过B1B2 继承过来的基类A 中的同名成员 fun0类的关系图及派生类的结构图如图所示其中“”号表示公有“-”号表示私有保护的用“#”号表示。 多层继承下的派生类关系图及成员构成图 对于派生类中成员 a 和 fun0 的访问只能通过直接基类 B1或者 B2 的名称来限定才可以不能通过基类A 来限定因为通过 A 限定无法表明成员是从 B1继承的还是从 B2 继承的。
例复杂版本的多继承同名隐藏示例。
#includeiostream
using namespace std;
class A
{public:int a;void fun0(){ coutA function is calledendl;}A(){coutconstruct Aendl;}~A(){coutdestruct Aendl;}
};
class B1:public A
{public:int b1;B1(){coutconstruct B1endl;}~B1(){coutdestruct B1endl;}
};
class B2:public A
{public:int b2;B2(){coutconstruct B2endl;}~B2(){coutdestruct B2endl;}
};
class C:public B1,public B2
{public:int c;void fun0(){ coutC function is calledendl;} C(){coutconstruct Cendl;}~C(){coutdestruct Cendl;}
};
int main()
{C c;c.B1::a10;c.B1::fun0() ;c.B2::a20;c.B2::fun0() ;return 0;
} 在主函数中定义了派生类对象 c如果只通过成员名称来访问该类的成员 a 和 fun0系统就无法唯一确定要引用的成员。这时必须通过作用域操作符通过直接基类来确定要访问的从基类 继承来的成员。 此时在内存中派生类对象同时拥有两个 a 的空间这两个a 可以分别通过 B1和B2调基类 A 的构造函数进行初始化能够存放不同的数值。也可以使用作用域操作符通过直接基类行区分分别进行访问。但是在大多数情况下我们不需要两个同名副本只需要保留一个即可C提供了虚基类技术来解决此问题。
五、虚基类 上例中当我们定义一个派生类对象 c 时它会构造 B1 类B2 类B1B2 类都有一个父类因此A 类被构造了两次在 c中A 中的数据成员 a 有两个副本A 中的成员函数 fun0 也有两个映射。一般可以将共同基类 A 设置为虚基类这时从不同的路径继承过来的同名数据成员在内存中就只有一个空间同一个函数名也只有一个映射在构造派生类对象 c 时A 类只会构造一次。
例虚基类应用示例。
#includeiostream
using namespace std;
class A
{public:int a;void fun0(){ coutA function is calledendl;}A(){coutconstruct Aendl;}~A(){coutdestruct Aendl;}
};
class B1:virtual public A
{public:int b1;B1(){coutconstruct B1endl;}~B1(){coutdestruct B1endl;}
};
class B2:virtual public A
{public:int b2;B2(){coutconstruct B2endl;}~B2(){coutdestruct B2endl;}
};
class C:public B1,public B2
{public:int c;void fun0(){ coutC function is calledendl;} C(){coutconstruct Cendl;}~C(){coutdestruct Cendl;}
};
int main()
{C c;c.a10; c.fun0();return 0;
} ⚡注意 虚基类并不是将基类声明为虚基类只是在类的派生过程中使用了 virtual 关键字 在具体程序设计过程中如果不需要重复的副本可以选择虚基类如果需要更多副本空间存在不同数据则可以采用作用域操作符方式区别访问。一般采用虚基类可以使得程序更加简洁同时节省更多内存空间。 六、总结
根据派生类继承基类的个数将继承分为单继承和多继承。多继承可以看成是单继承的组合。处于同一继承层次的各基类构造函数的调用顺序取决于定义派生类时所指定的基类的顺序与派生类构造函数中所定义的成员初始化列表顺序无关。如果在内层声明了同名标识符则外层标识符在内层不可见此时称内层标识符隐藏了外层同名标识符。若派生类中声明了与基类成员函数同名的新函数即使函数的形参表不同也不构成重载的关系。只有在相同作用域中定义的函数才可以构成重载。 通过作用域操作符明确且唯一地标识了派生类中由基类继承过来的成员解决了同名隐藏的问题。虚基类并不是将基类声明为虚基类只是在类的派生过程中使用了 virtual 关键字 。一般采用虚基类可以使得程序更加简洁同时节省更多内存空间。
七、共勉 以上就是我对C继承与派生——(8)多继承的理解希望本篇文章对你有所帮助也希望可以支持支持博主后续博主也会定期更新学习记录记录学习过程中的点点滴滴。如果有不懂和发现问题的小伙伴请在评论区说出来哦同时我还会继续更新对C继承与派生的理解请持续关注我哦