【C++初阶】继承(一)

张开发
2026/4/20 2:46:34 15 分钟阅读

分享文章

【C++初阶】继承(一)
博主名称键盘敲碎了雾霭 个人专栏: 《C语言》《数据结构》 《C》《Matlab》 《Python》⛺️指尖敲代码雾霭皆可破文章目录一、继承的定义1.1 概念1.2 父类访问变化1.3 继承类模版二、赋值兼容转化三、继承中的作用域四、派生类的默认成员函数4.1 构造函数4.2 拷贝构造4.3 赋值重载4.4 析构4.5 总结文章结语一、继承的定义1.1 概念继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段它允许我们在保持原有类特性的基础上进行扩展增加方法(成员函数)和属性(成员变量)这样产生新的类称派生类。继承呈现了面向对象程序设计的层次结构体现了由简单到复杂的认知过程。以前我们接触的函数层次的复用继承是类设计层次的复用定义格式:Person是基类也称作父类。Student是派生类也称作子类通过具体实例来理解#includeiostream#includestringusingnamespacestd;classParent{public:voididentity(){coutidentity()_nameendl;}protected:string _name张三;};classStudent:publicParent{public:voidstudy(){coutstudy()endl;}protected:string id;};classTeacher:publicParent{public:voidteach(){coutteach()endl;}protected:string _title;};intmain(){Student s1;Teacher t1;s1.identity();return0;}1.2 父类访问变化由于不同的继承方式继承基类成员访问方式的变化原来的成员变量会变成另外一种private除外类成员/继承方式public继承protected继承private继承基类的public成员派生类的public成员派生类的protected成员派生类的private成员基类的protected成员派生类的protected成员派生类的protected成员派生类的private成员基类的private成员在派生类中不可见在派生类中不可见在派生类中不可见基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。基类private成员在派生类中是不能被访问如果基类成员不想在类外直接被访问但需要在派生类中能访问就定义为protected。可以看出保护成员限定符是因继承才出现的。实际上面的表格我们进行一下总结会发现基类的私有成员在派生类都是不可见。基类的其他成员在派生类的访问方式Min(成员在基类的访问限定符继承方式)publicprotectedprivate.使用关键字class时默认的继承方式是private使用struct时默认的继承方式是public不过最好显示的写出继承方式。在实际运用中一般使用都是public继承几乎很少使用protetced/private继承也不提倡使用protetced/private继承因为protetced/private继承下来的成员都只能在派生类的类里面使用实际中扩展维护性不强。1.3 继承类模版基类是类模板时需要指定一下类域因为模版是按需实例化当成员函数未实例化时编译器找不到基类成员函数#defineContainvectornamespaceA{templateclassTclassstack:publicContainT{public:voidpush(constTx){ContainT::push_back(x);}voidpop(){ContainT::pop_back();}constTtop(){returnContainT::back();}size_tsize(){returnContainT::size();}boolempty(){returnContainT::empty();}};}intmain(){A::stackintst;}二、赋值兼容转化public继承的派生类对象 可以赋值给 基类的指针/基类的引用。有个形象的说法叫切片或者切割注意基类对象不能赋值给派生类对象子类有的父类没有强转也是不可以的。基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。Student s;Parenttemps;//不是隐式类型转化如果是的话会产生临时变量不支持引用Parent*temp1s;Parent temp2s;三、继承中的作用域在继承体系中基类和派生类都有独立的作用域派生类和基类中有同名成员派生类成员将屏蔽基类对同名成员的直接访问这种情况叫隐藏。在派生类成员函数中可以使用基类:基类成员显示访问classParent{protected:int_num10;};classStudent:publicParent{public:voidFuc(){cout_numendl;//优先访问当前作用域的coutParent::_numendl;}protected:int_num100;};intmain(){Student st;st.Fuc();}需要注意的是如果是成员函数的隐藏只需要函数名相同就构成隐藏。不是重载不在同一作用域classParent{public:voidFuc1(){couthahaendl;}};classStudent:publicParent{public:voidFuc1(inti){coutheheendl;}};intmain(){Student st;st.Parent::Fuc1();}注意在实际中在继承体系里面最好不要定义同名的成员。四、派生类的默认成员函数关于默认成员函数不熟悉的小伙伴可以通过这篇文章来了解下【C初阶】类和对象二默认成员函数详解与日期类完整实现4.1 构造函数默认生成的构造函数的行为内置类型-不确定自定义类型-调用默认构造继承父类成员看做一个整体对象要求调用父类的默认构造派生类的构造函数必须调基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数则必须在派生类构造函数的初始化列表阶段显示调用具体实例classParent{public:Parent(string name):_name(name){}protected:string _name;};classStudent:publicParent{public:Student(string id,string name,intscore):_id(id)//,_name(name),Parent(name),_score(score){}protected:string _id;int_score100;};intmain(){Students(123,lishi,60);return0;}4.2 拷贝构造默认生成的拷贝构造函数的行为内置类型-值自定义类型-调用默认构造拷贝继承父类成员看做一个整体对象要求调用父类的默认拷贝构造与赋值重载、析构类似当有资源释放时才需要写拷贝构造派生类的拷构造函数必须调基类的拷贝构造完成基类的拷贝初始化如果不调用父类的拷贝构造会调用默认构造不满足需求。classParent{public:Parent(string name):_name(name){}Parent(constParents){coutParent(Parent s)endl;}protected:string _name;};classStudent:publicParent{public:Student(string id,string name,intscore):_id(id)//,_name(name),Parent(name),_score(score){}Student(constStudents):_id(s._id),_score(s._score),Parent(s){}protected:string _id;int_score100;//int* _ptr new[20];};intmain(){Students(123,lishi,60);Students1(s);return0;}从声明的角度初始化列表会先调父类的拷贝构造按内存的顺序父类在前面4.3 赋值重载严格说student赋值重载默认生成的就够用了如果有需要深拷贝的资源才需要自己实现派类的operator必须要调基类的operator完成基类的复制。需要注意的是派类的operator隐藏了基类的operator所以显示调基类的operator需要指定基类作域classParent{public:Parent(string name):_name(name){}Parent(constParents){coutParent(Parent s)endl;}Parentoperator(constParents){coutPerson operator(const Person p)endl;if(this!s)_names._name;return*this;}protected:string _name;};classStudent:publicParent{public:Student(string id,string name,intscore):_id(id)//,_name(name),Parent(name),_score(score){}Student(constStudents):_id(s._id),_score(s._score),Parent(s){}Studentoperator(constStudents){if(this!s){_ids._id;_scores._score;Parent::operator(s);return*this;}}protected:string _id;int_score100;//int* _ptr new[20];};intmain(){Students(123,lishi,60);Students1(s);Students2(s);s1s2;return0;}4.4 析构如果没有资源申请不需要在子类显示写析构函数父类会自动调析构函数生类的析构函数会在被调完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序派生类对象初始化先调用基类构造再调派生类构造。派生类对象析构清理先调用派生类析构再调基类的析构。补充子类的析构和父类的析构构成隐藏关系编译器会对析构函数名进行特殊处理都处理成destructor()4.5 总结构造阶段 (创建对象时)先调用基类构造函数再调用派生类构造函数。析构阶段 (销毁对象时)先调用派生类析构函数再调用基类析构函数文章结语感谢你读到这里我是「键盘敲碎了雾霭」愿这篇文字帮你敲开了技术里的小迷雾 如果内容对你有一点点帮助不妨给个暖心三连吧点赞| ❤️收藏| ⭐关注听说三连的小伙伴代码一次编译过bug绕着走你的支持就是我继续敲碎技术雾霭的最大动力 小彩蛋/^ ^\ / 0 0 \ V\ Y /V / - \ / | V__) ||摸一摸毛茸茸的小狗赶走所有疲惫和bug我们下篇见 ✨

更多文章