实际中最好不定义同名函数
发布时间:2025-06-24 20:39:44 作者:北方职教升学中心 阅读量:147
什么是继承?
继承(inheritance)机制是⾯向对象程序设计使代码可以复⽤的最重要的⼿段,它允许我们在保持原有类特性的基础上进⾏扩展,增加⽅法(成员函数)和属性(成员变量),这样产⽣新的类,称派⽣类。
派生类的析构函数会在被调用完成之后自动调用基类的析构函数清理基类成员;(这样才能保证派生类对象先清理派生类的那一部分成员,再清理基类成员这一顺序)。
voidtest2(){Student sobj;// 1.派⽣类对象可以赋值给基类的指针/引⽤Person*pp =&sobj;Person&rp =sobj;// ⽣类对象可以赋值给基类的对象是通过调⽤后⾯会讲解的基类的拷⻉构造完成的Person pobj =sobj;//2.基类对象不能赋值给派⽣类对象,这⾥会编译报错//sobj = pobj;}
四、
private
成员在派生类中不能被访问;如果基类成员只是不想在类外直接被访问,而在派生类中能被访问,就定义为protected
。继承中的作用域1、
方法一:
//基类构造函数私有化classTest{public:voidfunc(){cout <<"构造函数私有化,派生类无法实例化出对象"<<endl;}protected:int_i =1;private:Test(){}};classFun:publicTest{public:Fun(){}private:char_ch;};
方法二:
//final修饰classBasefinal{public:voidfun(){cout <<"final修饰,无法被继承"<<endl;}protected:int_i;char_ch;};classDiv:publicBase{};
六、
比如下面这连个类,学生和老师,它们之间有很多相同的成员变量(函数):
classStudent{public:voidstudying(){}//学习voididentity(){}//进校园private:string _name;// 姓名int_age =18;// 年龄string _address;// 地址string _tel;// 电话int_id;//学号};classTeacher{public:voidteaching(){}//教学voididentity(){}//进校园private:string _name;// 姓名int_age =18;// 年龄string _address;// 地址string _tel;// 电话string _title;// 职称};
对于这种情况,我们就可以将这些公共成员放到一个类Person
中,让Student
和Teacher
去继承Person
类的这些公共成员。(既叫基类/派生类,也叫父类/子类)
1、继承
和多态
本篇博客来学习C++中的继承,加油!
一、继承与组合
public
继承是一种is-a
;就是每一个派生类对象都是一个基类对象- 组合呢,是
has-a
的关系;这里假设B组合了A,每一个B对象中都有一个A对象。
本篇博客来学习C++中的继承,加油!
public
继承是一种is-a
;就是每一个派生类对象都是一个基类对象- 组合呢,是
has-a
的关系;这里假设B组合了A,每一个B对象中都有一个A对象。
这里使用继承来实现一下stack
:
template<classT>classstack:publicvector<T>{//这里编译器是按需进行实例化,这里只是实例化出vector<T>//里面对应的成员函数等,都是按需实例化,在需要时才进行实例化//所以需要指定类域来访问vector<T> 的成员函数voidpush(constT&x){// 模版是按需实例化,push_back等成员函数未实例化,所以找不到vector<T>::push_back(x);}voidpop(){vector<T>::pop_back();}constT&top(){returnvector<T>::back();}boolempty(){returnvector<T>::empty();}};
三、以后再了解这部分内容)。继承中的友元
首先,友元关系不能被继承;也就基类的友元函数无法访问派生类的私有和保护成员。
八、3、相关选择题解析
classA{public:voidfun(){cout <<"func()"<<endl;}};classB:publicA{public:voidfun(inti){cout <<"func(int i)"<<i <<endl;}};intmain(){B b;b.fun(10);b.fun();return0;};
首先,对于成员函数,只要函数名相同就构成隐藏,使用A类与B类中fun
函数构成隐藏。隐藏规则
- 在继承中,基类和派生类都有独立的作用域。
2、
其实这个也很好理解,静态成员存储在常量区,只存在一个。以前我们接触的函数层次的复⽤,继承是类设计层次的复⽤。
黑箱复用
;(对象的内部细节不可见,组合类之间没有很强的依赖关系,耦合度低,使用对象组合,有助于保持每个类的封装。多继承与菱形继承1、定义格式
2、番外篇:实现一个不能被继承的类
方法一:基类的构造函数私有,派生类的构造必须调用基类的构造函数,但是基类的构造函数私有化,派生类就不能调用,就无法实例化出对象。
对于第二道题,我第一时间想法:正常运行,(个人理解:B类中继承了A类的
fun()
成员函数,又实现了自己的成员函数fun(int)
形成了函数重载);很显然这种理解是错误的,(在继承中,派生类中会屏蔽基类中同名函数的直接访问);我们无法通过派生类对象直接访问基类的成员函数。
五、派生类的默认成员函数
1、(可以看出保护成员限定符protected
就是因继承而出现的)。基类的其他成员在派生类发访问方式 就等与成员在基类的访问限定符与继承方式之中最小的 (public
> protected
> private
)。(多继承,一定会存在菱形继承;java
中不支持多继承就规避了这个问题;所以不推荐设计出这种菱形继承的模型)。继承基类成员访问方式
在看基类成员访问方式之前,先来看一个在【C++类和对象(上)】—— 我与C++的不解之缘(三)-CSDN博客 提到的访问限定符,当初没有去了解它是啥,现在来看一下
//基类classA{public:int_i1 =1;protected:int_i2 =2;private:int_i3 =3;};// public 继承//派生类classB:publicA{public:voidfunc1(){//基类中的public成员,派生类中可以直接访问,在类外也可以直接访问cout <<_i1 <<endl;}voidfunc2(){//基类中的protected成员,派生类中可以直接访问,在类外不能直接访问cout <<_i2 <<endl;}//void func3()//{// //基类中的private成员,派生类中不可见, 派生类中和类外都不能访问// cout << _i3 << endl;//}};voidtest1(){B bb;//基类的public成员, 在类外可以直接访问cout <<bb._i1 <<endl;bb.func2();}
基类成员 / 继承方式 | public 继承 | protected 继承 | private 继承 |
---|---|---|---|
public 成员 | 派生类中的public 成员 | 派生类中的protected 成员 | 派生类中的private 成员 |
protected 成员 | 派生类中的protected 成员 | 派生类中的protected 成员 | 派生类中的private 成员 |
private 成员 | 在派生类中不可见 | 在派生类在不可见 | 在派生类在不可见 |
- 基类
private
成员在派生类中无论以什么方式继承都是不可见的;(这里不可见指的就是私有成员还是被继承到派生类当中去,但是语法上,无论是在派生类里面还是类外面都不能直接去访问它。- 根据上面表格,可以发现: 基类的
private
成员在派生类当中都是不可见的。- 派生类的
operator=
函数必须要调用基类的operator=
函数完成基类的赋值(**注意:**派生类的operator=
隐藏了基类的operator=
函数,所以在调用时需要指定类域)。- 派生类对象析构清理先调用派生类析构再调用基类的析构。
- 派生类的构造函数必须调用基类的构造函数来初始化基类的那部分成员,如果基类没有默认构造函数,就必须在派生类构造函数的初始化列表显示调用。
- 注意:对于成员函数,只要函数名相同就构成隐藏。默认成员函数
现在再来看一下默认成员函数:
所谓默认成员函数(6个),就是我们不写编译器会自动生成;那在派生类当中,这写默认成员函数是如何生成的呢。
classperson{protected:string _name ="小晓";int_num =111;};classstudent:publicperson{public:voidtest(){cout <<"_name: "<<_name <<endl;cout <<"_num: "<<_num <<endl;cout <<"person::_num: "<<person::_num <<endl;}protected:int_num =999;};voidtest3(){student s1;s1.test();}intmain(){test3();return0;}
2、
destructor
,所以基类的析构函数不加virtual
的情况下,派生类析构函数和基类析构函数构成隐藏。白箱复用
;(**白箱复用相对于可视性而言:**在继承方式中,基类的内部细节对派生类可见;继承一定程度上破坏了基类的封装,基类的改变对派生类有很大影响;基类和派生类之间耦合性高,依赖性很强)。**菱形继承: ** 菱形继承属于多继承的一种特殊情况,菱形继承存在数据冗余和二义性的问题;在Assistant
的对象中,Person
类的成员会存在两份。有了多继承,就存在菱形继承,有了菱形继承就有
菱形虚拟继承,底层实现就很复杂,性能也会有⼀些损失,所以最好不要设计出菱形继承。
简单来说,对于两个类(甚至多个),它们直接有一些相同的成员;这样设计是有些冗余的。虚继承
虚继承可以说是解决菱形继承这个问题的;
很多⼈说C++语法复杂,其实多继承就是⼀个体现。
2、继承中的静态成员 基类定义了static
静态成员,在整个继承体系中有且只有一个这样的成员;无论有多少派生类都只有一个static
静态成员。
前言:
面向对象编程语言的三大特性:封装
、
派生类对象初始化先调用基类的构造函数,再调用派生类的构造函数。基类和派生类之间的转换public
继承的派生类对象,可以赋值给基类的 指针
/引用
;这种我们可以形象的称为**切片(或者切割) **,简单来说就是将派生类中基类的那一部分切出来,基类的指针
/引用
指向派生类中切出来的那一部分。
3、IO库中的虚拟菱形继承

template<classCharT,classTraits=std::char_traits<CharT>>classbasic_ostream:virtualpublicstd::basic_ios<CharT,Traits>{};template<classCharT,classTraits=std::char_traits<CharT>>classbasic_istream:virtualpublicstd::basic_ios<CharT,Traits>{};
九、继承类模板继承类模板:
如果继承的基类是类模板,则需要指定类域;否则就会报错。多继承可
以认为是C++的缺陷之⼀,后来的⼀些编程语⾔都没有多继承,如Java。
基类对象不能赋值给派生类对象 基类的指针
/引用
可以通过强制转换赋值给派生类的指针和引用,但必须是基类的指针指向派生类对象时才是安全的。(这里如果基类是多态类型,可以使用RTTI
和dynamic_cast
进行识别后进行安全转换。继承呈现了⾯向对象程序设计的层次结构,体现了由简单到复杂的认知过程。Student是派⽣类,也称作子类。classPerson{voididentity(){}//进学校protected:string _name;// 姓名int_age =18;// 年龄string _address;// 地址string _tel;// 电话};classStudent:publicPerson{public:voidstudy(){};private:string _id;//学号};classTeacher:publicPerson{voidteaching(){}//教学private:string _title;//职称};
这样就形成了下图这样的继承关系

二、classPerson{public:string _name;int_age;string _address;};// 使⽤虚继承Person类classStudent:virtualpublicPerson{protected:int_num;//学号};// 使⽤虚继承Person类classTeacher:virtualpublicPerson{protected:int_id;// 职⼯编号};// 教授助理classAssistant:publicStudent,publicTeacher{protected:string _majorCourse;// 主修课程};intmain(){// 使⽤虚继承,可以解决数据冗余和⼆义性Assistant a;a._name ="peter";return0;}
简单来说,不推荐设计出菱形继承。
优先去使用组合,而不是继承,组合耦合度低,代码维护性好;不过类之间如果关系适合继承(is-a
)那就使用继承,多态也要实现继承; 继承部分到这里就结束了,感谢各位的支持,继续加油!!!
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws
继承模型
基类定义了static
静态成员,在整个继承体系中有且只有一个这样的成员;无论有多少派生类都只有一个static
静态成员。
前言:
面向对象编程语言的三大特性:封装
、
public
继承的派生类对象,可以赋值给基类的指针
/引用
;这种我们可以形象的称为**切片(或者切割) **,简单来说就是将派生类中基类的那一部分切出来,基类的指针
/引用
指向派生类中切出来的那一部分。
3、IO库中的虚拟菱形继承
template<classCharT,classTraits=std::char_traits<CharT>>classbasic_ostream:virtualpublicstd::basic_ios<CharT,Traits>{};template<classCharT,classTraits=std::char_traits<CharT>>classbasic_istream:virtualpublicstd::basic_ios<CharT,Traits>{};
九、继承类模板继承类模板:
如果继承的基类是类模板,则需要指定类域;否则就会报错。多继承可
以认为是C++的缺陷之⼀,后来的⼀些编程语⾔都没有多继承,如Java。
继承类模板:
如果继承的基类是类模板,则需要指定类域;否则就会报错。多继承可
以认为是C++的缺陷之⼀,后来的⼀些编程语⾔都没有多继承,如Java。
指针
/引用
可以通过强制转换赋值给派生类的指针和引用,但必须是基类的指针指向派生类对象时才是安全的。(这里如果基类是多态类型,可以使用RTTI
和dynamic_cast
进行识别后进行安全转换。继承呈现了⾯向对象程序设计的层次结构,体现了由简单到复杂的认知过程。Student是派⽣类,也称作子类。classPerson{voididentity(){}//进学校protected:string _name;// 姓名int_age =18;// 年龄string _address;// 地址string _tel;// 电话};classStudent:publicPerson{public:voidstudy(){};private:string _id;//学号};classTeacher:publicPerson{voidteaching(){}//教学private:string _title;//职称};
这样就形成了下图这样的继承关系
二、classPerson{public:string _name;int_age;string _address;};// 使⽤虚继承Person类classStudent:virtualpublicPerson{protected:int_num;//学号};// 使⽤虚继承Person类classTeacher:virtualpublicPerson{protected:int_id;// 职⼯编号};// 教授助理classAssistant:publicStudent,publicTeacher{protected:string _majorCourse;// 主修课程};intmain(){// 使⽤虚继承,可以解决数据冗余和⼆义性Assistant a;a._name ="peter";return0;}
简单来说,不推荐设计出菱形继承。
classPerson{public:string _name;int_age;string _address;};// 使⽤虚继承Person类classStudent:virtualpublicPerson{protected:int_num;//学号};// 使⽤虚继承Person类classTeacher:virtualpublicPerson{protected:int_id;// 职⼯编号};// 教授助理classAssistant:publicStudent,publicTeacher{protected:string _majorCourse;// 主修课程};intmain(){// 使⽤虚继承,可以解决数据冗余和⼆义性Assistant a;a._name ="peter";return0;}
简单来说,不推荐设计出菱形继承。
is-a
)那就使用继承,多态也要实现继承;继承部分到这里就结束了,感谢各位的支持,继续加油!!!
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws
单继承:一个派生类只有一个基类
多继承:应该派生类有两个或以上直接基类;(多继承对象在内存中的模型:先继承的在基类在前面,后继承的基类在后面,派生类成员放到最后。
方法二C++11 新增关键字
final
, final修饰基类,派生类就不能继承的。
classPerson{public:friendvoidDisplay(constPerson&p,constStudent&s);protected:string _name;// 姓名};classStudent:publicPerson{protected:int_stuNum;// 学号};voidFunc(constPerson&p,constStudent&s){cout <<p._name <<endl;cout <<s._stuNum <<endl;}
七、继承的定义
我们看到Person是基类,也称作父类。
在实际应用中,一般都是使用 public
继承,很少使用protected
和private
继承(不推荐,因为protected
和private
继承下来的成员只能在派生类中使用,实际中扩展维护性不是很强)。实际中最好不定义同名函数。