继承喵文章目录前言一、继承是什么的喵二、继承方式的符号价值与使用方式喵三类模板的继承喵四父类与子类对象赋值兼容转换(也叫切片喵)五继承的作用域喵六子类中默认成员函数的生成方式喵1.子类的默认构造函数要做哪些事了喵2.显式构造函数怎么写呢喵3.默认拷贝构造和重载和析构函数呢喵4.最后是析构喵三个小知识点喵1.让父类无法被子类继承的方式喵2.友元关系不能被继承3.父类中的static成员子类中八单继承与多继承喵九继承(is_a)与组合(has_a)喵总结前言本篇文章会一点一点的深入讲解继承的概念运用和底层喵希望大家看看喵。一、继承是什么的喵当我们在构建一个类却想使用另一个类的内容或几个类之间有重复部分我们想要把这部分提取出来多次使用时就会使用到继承了喵。(下面是一个使用案例喵)#includeiostream using namespace std; class person { public: person(string d dsd) :_name(d) { } string _name; }; class student :public person { public: student(int a 10,string s jsf) :_num(a) ,person(s) { } int _num; }; class teacher :public person { public: teacher(int b 20,string s djaq) :_id(b) ,person(s) { } int _id; }; int main() { //teacher和student都共用person这个类实现了 //代码的简化喵 teacher a(20,yinli); student b(30,xiaoliao); cout a._name a._id endl; cout b._name b._num endl; return 0; }二、继承方式的符号价值与使用方式喵1.继承的符号与访问限定符的关键字是一样的喵(public private protected)。2.继承的叫父类(基类)被继承的叫子类(衍生类)喵。3.父类的private成员在子类中是完全不可见(即无法访问)在类外面也是不可访问的喵不过父类的private成员还是继承到了子类中的喵。4.父类的protected对象也是不可以在类外面访问但是可以在子类中访问的喵因此protected是为了继承才出现的喵。struct A { int _a; protected: int _name; private: int _score; }; struct B :public A { void func() { //A中的protected成员可以访问喵 _name 20; //A中的private成员不可以访问喵 _score 10; } }; int main() { B a; a._a 20; //类外A中的private和protected成员都是不可以访问喵 a._name 30; a._score 30; return 0; }如果想要调用父类的private/或在类外调用父类的protected就只能在父类内部设计一个public之类的含private成员的函数来间接调用了喵。5.子类访问父类成员点的最高权限min(父类中成员的访问限定符修饰,继承方式限定)喵例protected成员public的继承子类访问权限为protected喵protected成员private的继承子类访问权限为private喵private成员public的继承子类访问权限为private喵一般都是写public继承喵class student:person //不显示写继承也是有默认继承的喵父类为struct时是public喵为class时是private喵。三类模板的继承喵例stack用vector继承实现喵#includevector namespace gal { templateclass T class stack : vectorT { public: void push(const T x) { vectorT::push_back(x); } }; } int main() { gal::stackint st; st.push(4); return 0; }模板时不能像常规函数那样直接调用喵因为模板中的函数是没有实例化的喵但创建stack变量时会调用vector的构造函数(也会调用stack的构造函数哦喵)是T明确vectorT::就能发送T的类型来实例化这个函数了喵。四父类与子类对象赋值兼容转换(也叫切片喵)也就是子类的对象可以赋值给父类的对象(指针地址/引用的传递都是可以的喵)但是父类反过来是不行的喵。(也就是可以大转小喵)(这个过程是不涉及类型转换的喵涉及的话下面的引用处就应该报错了喵(引用临时变量))struct A { A(int a 30,int b 90) :_a(a) ,_b(b) { } int _a 20; int _b 30; }; struct B :A { //其实B什么也不写也时刻的喵 B(int a 50,int b 70) :_c(a) ,A(a,b) { } int _c 40; }; int main() { B b(60,70); A a(40,60); //b的值可以赋值给a, a b; cout a._a a._b endl; //b的地址可以给A*的指针喵 A* p b; //b的引用可以给A的引用喵 A q b; return 0; }本质可以算是b中的那块A类型的空间直接切给了a使用喵因此也叫切片喵。父类的指针和引用也是可以通过强制类型转换给子类使用的喵(不可以值拷贝喵)用途就是如果该父类的指针或引用指向的就是子类的空间或变量的话就能重新再给子类的指针或引用数据喵。因此可以将B中A的部分直接给A类型的变量使用喵.五继承的作用域喵意义子类和父类是在两个不同的作用域中的喵。同时子类和父类有同名变量或同名函数时父类的同名变量或函数会被隐藏喵。struct A { int _a 30; }; struct B :A { int _a 40; }; int main() { B a; //默认调用的是B中的喵 cout a._a endl; //特定作用域后就能调用A中的了喵 cout a.A::_a endl; }下面是一道题喵class A { public: void fun() { cout func() endl; } }; class B : public A { public: void fun(int i) { cout func(int i) iendl; } }; int main() { B b; b.fun(10); b.fun(); return 0; };这题会报错喵A和B中的fun函数不构成重载关系(因为是两个作用域喵)而是隐藏关系喵所以b.fun()这里没有调用参数编译器会报错的喵。只有显式调用A中的fun()才行喵。六子类中默认成员函数的生成方式喵1.子类的默认构造函数要做哪些事了喵1对内置类型成员不明确的初始化喵。2对类类型成员调用其默认构造。3将父类视为一个整体调用其的默认构造函数。2.显式构造函数怎么写呢喵在构造函数中即使没有显式调用父类的构造函数编译器也会在初始化列表中调用其的默认构造函数喵。其没有默认构造函数时我们必须自己调用父类的默认构造函数喵。struct father { father(string a) :_name(a) { } string _name; }; struct child : father { child(string s,int a,string h) //显式调用构造函数喵 //默认构造编译器会自己调喵 //写法就是写父类的类型名加参数喵 :father(s) ,_score(a) ,_gal(h) { } int _score; string _gal; }; int main() { child c(galgame,10,love); cout c._gal c._name c._score endl; return 0; }3.默认拷贝构造和重载和析构函数呢喵默认拷贝构造是内置类型值拷贝类类型成员和父类调用其的拷贝构造函数喵。因此除非有深拷贝的需求不然编译器自动生成的拷贝构造函数就够用了喵。下面给一个显示构造的写法struct person { person(string s) :_s(s) { } string _s; }; struct student:person { student(string s1,string s2,int n) :person(s1) ,_s1(s2) ,_num(n) { } student(const student x) //直接写就行了喵有切片帮助喵 //父类调用最好放第一行喵 //符合声明顺序喵 :person(x) ,_num(x._num) ,_s1(x._s1) { } int _num; string _s1; }; int main() { student a(galgame, love, 100); student b(a); cout b._s b._s1 b._num endl; return 0; }显式调用的情况下必须要调父类和类类型成员的拷贝构造函数不然编译器就自动调到默认构造函数造成数据错误了喵。默认重载的机制与默认拷贝构造函数的机制一样喵显式重载的话也要将全部都显式调用喵。struct person { person(string s) :_s(s) { } person operator(const person x) { if (this ! x) { _s x._s; } return *this; } string _s; }; struct student:person { student(const string s1,const string s2,int n) :person(s1) ,_s1(s2) ,_num(n) { } student(const student x) :person(x) ,_num(x._num) ,_s1(x._s1) { } student operator(const student x) { if (this ! x) { //调用person的重载要确认作用域喵 //因为student和person的operator名字相同构成隐藏喵 person::operator(x); _num x._num; _s x._s; } return *this; } int _num; string _s1; }; int main() { student a(galgame, love, 100); student b(a); cout b._s b._s1 b._num endl; student c(yinli, miaomiao,200); b c; cout b._s b._s1 b._num endl; return 0; }4.最后是析构喵析构中所有类的析构函数的析构名都会被处理成destructor()函数名因此子类和父类的析构函数也构成隐藏关系喵。正常情况下都是不用我们写析构的喵。编译器自己生成的就会调用父类和类类型成员的析构了喵。值得注意的一点是无论显式调用的析构中是否有调用父类的析构函数编译器在析构函数结束前依旧会再调用一次父类和类类型成员的析构喵。如下面喵C是一个类类型成员喵上面的这种代码就代表了会分别调用两次person和C的析构函数喵。因此析构函数是真的不用我们去调用父类和类类型成员的析构的喵。七三个小知识点喵1.让父类无法被子类继承的方式喵(1)让父类的构造函数私有化喵struct A { private: A() { } int _a 30; }; struct B : A { int _b 80; }; int main() { B a; return 0; }会报错喵不创建a变量就不会error了喵(2)给父类的名字后面加个final喵struct A final { A() { } int _a 30; }; struct B : A { int _b 80; }; int main() { //这种不创建变量也会error喵 //B a; return 0; }2.友元关系不能被继承struct A { friend void func(); private: int _a 20; int _b 40; }; struct B :A { private: int _c 30; }; void func() { A a; B c; cout can use it endl; cout a._a a._b endl; //上面是可以的但_c是B的变量func是A的友元但不是B的. cout c._c endl; }3.父类中的static成员子类中共用struct A { static int a; int b 40; }; struct B : A { int b 30; }; int A::a 10; int main() { A a; B b; //一个值 cout A::a endl; cout B::a endl; //一个地址 cout a.a endl; cout b.a endl; //这里说明这是两个变量 //且常规变量是不共用的喵 cout a.b endl; cout b.b endl; return 0; }八单继承与多继承喵单继承一个子类只有一个直接父类喵。多继承一个子类有多个直接父类喵。struct person { string s; }; struct teacher { string h; }; //单继承 struct student :person { string d; }; //单继承 struct man :student { string l; }; //多继承 struct assistant :public person, public teacher { string c; };菱形继承喵菱形继承的问题喵数据冗杂和二义性喵数据冗杂animal有两份喵二义性调用时不知道animal是哪一个喵struct person { string _name fs; }; struct student:person { string _big op; }; struct teacher :person { string _small jj; }; struct assistant :student, teacher { string _galgame oo; }; int main() { assistant a; cout a._name endl; return 0; }a._name的调用不明确喵。只能指定作用域调用喵。最经典的菱形调用解决方法喵虚继承 virtual(写在具有二义性的地方)此时student和teacher会共用一个person了。虚继承两边都要写喵不然会另一个virtual就是无效的喵。只要有二义性和数据冗杂的都叫菱形继承喵。总之一定不要设计出菱形继承啊喵。补充道调试下assistant会先走person的构造(越早的类越早调喵)在student和teacher中的person构造编译器会直接跳过喵。xiamian下面来一道指针偏离的问题喵。class Base1 { public: int _b1; }; class Base2 { public: int _b2; }; class Derive : public Base1, public Base2 { public: int _d; }; int main() { Derive d; Base1* p1 d; Base2* p2 d; Derive* p3 d; return 0; }因此p1p3!p2喵。九继承(is_a)与组合(has_a)喵//这是继承 struct stack :public list {}; //这是组合 struct stack { list it; };is_a和has_a就是继承和组合的别名喵。耦合度几者之间的关系密切程度喵。组合就是车有轮胎强调有的关系喵(一般耦合度较低喵)。继承就是galgame是视觉小说游戏强调是的关系(一般耦合度较高喵)。可以is_a也可以has_a时优先使用has_a喵。补充喵模板特化中如int max(int a,int b) int maxint(int a,int b) int main(){ max(1020); maxint(10,20); return 0;}调用中没有模板时优先调用常规函数有模板变量申明时一定是走的实例化喵。总结写不动了喵燃尽了喵。继承的访问限定符的类模板的切片的作用域的默认函数的单多继承喵。
萝莉控也能懂得C++继承逻辑喵
继承喵文章目录前言一、继承是什么的喵二、继承方式的符号价值与使用方式喵三类模板的继承喵四父类与子类对象赋值兼容转换(也叫切片喵)五继承的作用域喵六子类中默认成员函数的生成方式喵1.子类的默认构造函数要做哪些事了喵2.显式构造函数怎么写呢喵3.默认拷贝构造和重载和析构函数呢喵4.最后是析构喵三个小知识点喵1.让父类无法被子类继承的方式喵2.友元关系不能被继承3.父类中的static成员子类中八单继承与多继承喵九继承(is_a)与组合(has_a)喵总结前言本篇文章会一点一点的深入讲解继承的概念运用和底层喵希望大家看看喵。一、继承是什么的喵当我们在构建一个类却想使用另一个类的内容或几个类之间有重复部分我们想要把这部分提取出来多次使用时就会使用到继承了喵。(下面是一个使用案例喵)#includeiostream using namespace std; class person { public: person(string d dsd) :_name(d) { } string _name; }; class student :public person { public: student(int a 10,string s jsf) :_num(a) ,person(s) { } int _num; }; class teacher :public person { public: teacher(int b 20,string s djaq) :_id(b) ,person(s) { } int _id; }; int main() { //teacher和student都共用person这个类实现了 //代码的简化喵 teacher a(20,yinli); student b(30,xiaoliao); cout a._name a._id endl; cout b._name b._num endl; return 0; }二、继承方式的符号价值与使用方式喵1.继承的符号与访问限定符的关键字是一样的喵(public private protected)。2.继承的叫父类(基类)被继承的叫子类(衍生类)喵。3.父类的private成员在子类中是完全不可见(即无法访问)在类外面也是不可访问的喵不过父类的private成员还是继承到了子类中的喵。4.父类的protected对象也是不可以在类外面访问但是可以在子类中访问的喵因此protected是为了继承才出现的喵。struct A { int _a; protected: int _name; private: int _score; }; struct B :public A { void func() { //A中的protected成员可以访问喵 _name 20; //A中的private成员不可以访问喵 _score 10; } }; int main() { B a; a._a 20; //类外A中的private和protected成员都是不可以访问喵 a._name 30; a._score 30; return 0; }如果想要调用父类的private/或在类外调用父类的protected就只能在父类内部设计一个public之类的含private成员的函数来间接调用了喵。5.子类访问父类成员点的最高权限min(父类中成员的访问限定符修饰,继承方式限定)喵例protected成员public的继承子类访问权限为protected喵protected成员private的继承子类访问权限为private喵private成员public的继承子类访问权限为private喵一般都是写public继承喵class student:person //不显示写继承也是有默认继承的喵父类为struct时是public喵为class时是private喵。三类模板的继承喵例stack用vector继承实现喵#includevector namespace gal { templateclass T class stack : vectorT { public: void push(const T x) { vectorT::push_back(x); } }; } int main() { gal::stackint st; st.push(4); return 0; }模板时不能像常规函数那样直接调用喵因为模板中的函数是没有实例化的喵但创建stack变量时会调用vector的构造函数(也会调用stack的构造函数哦喵)是T明确vectorT::就能发送T的类型来实例化这个函数了喵。四父类与子类对象赋值兼容转换(也叫切片喵)也就是子类的对象可以赋值给父类的对象(指针地址/引用的传递都是可以的喵)但是父类反过来是不行的喵。(也就是可以大转小喵)(这个过程是不涉及类型转换的喵涉及的话下面的引用处就应该报错了喵(引用临时变量))struct A { A(int a 30,int b 90) :_a(a) ,_b(b) { } int _a 20; int _b 30; }; struct B :A { //其实B什么也不写也时刻的喵 B(int a 50,int b 70) :_c(a) ,A(a,b) { } int _c 40; }; int main() { B b(60,70); A a(40,60); //b的值可以赋值给a, a b; cout a._a a._b endl; //b的地址可以给A*的指针喵 A* p b; //b的引用可以给A的引用喵 A q b; return 0; }本质可以算是b中的那块A类型的空间直接切给了a使用喵因此也叫切片喵。父类的指针和引用也是可以通过强制类型转换给子类使用的喵(不可以值拷贝喵)用途就是如果该父类的指针或引用指向的就是子类的空间或变量的话就能重新再给子类的指针或引用数据喵。因此可以将B中A的部分直接给A类型的变量使用喵.五继承的作用域喵意义子类和父类是在两个不同的作用域中的喵。同时子类和父类有同名变量或同名函数时父类的同名变量或函数会被隐藏喵。struct A { int _a 30; }; struct B :A { int _a 40; }; int main() { B a; //默认调用的是B中的喵 cout a._a endl; //特定作用域后就能调用A中的了喵 cout a.A::_a endl; }下面是一道题喵class A { public: void fun() { cout func() endl; } }; class B : public A { public: void fun(int i) { cout func(int i) iendl; } }; int main() { B b; b.fun(10); b.fun(); return 0; };这题会报错喵A和B中的fun函数不构成重载关系(因为是两个作用域喵)而是隐藏关系喵所以b.fun()这里没有调用参数编译器会报错的喵。只有显式调用A中的fun()才行喵。六子类中默认成员函数的生成方式喵1.子类的默认构造函数要做哪些事了喵1对内置类型成员不明确的初始化喵。2对类类型成员调用其默认构造。3将父类视为一个整体调用其的默认构造函数。2.显式构造函数怎么写呢喵在构造函数中即使没有显式调用父类的构造函数编译器也会在初始化列表中调用其的默认构造函数喵。其没有默认构造函数时我们必须自己调用父类的默认构造函数喵。struct father { father(string a) :_name(a) { } string _name; }; struct child : father { child(string s,int a,string h) //显式调用构造函数喵 //默认构造编译器会自己调喵 //写法就是写父类的类型名加参数喵 :father(s) ,_score(a) ,_gal(h) { } int _score; string _gal; }; int main() { child c(galgame,10,love); cout c._gal c._name c._score endl; return 0; }3.默认拷贝构造和重载和析构函数呢喵默认拷贝构造是内置类型值拷贝类类型成员和父类调用其的拷贝构造函数喵。因此除非有深拷贝的需求不然编译器自动生成的拷贝构造函数就够用了喵。下面给一个显示构造的写法struct person { person(string s) :_s(s) { } string _s; }; struct student:person { student(string s1,string s2,int n) :person(s1) ,_s1(s2) ,_num(n) { } student(const student x) //直接写就行了喵有切片帮助喵 //父类调用最好放第一行喵 //符合声明顺序喵 :person(x) ,_num(x._num) ,_s1(x._s1) { } int _num; string _s1; }; int main() { student a(galgame, love, 100); student b(a); cout b._s b._s1 b._num endl; return 0; }显式调用的情况下必须要调父类和类类型成员的拷贝构造函数不然编译器就自动调到默认构造函数造成数据错误了喵。默认重载的机制与默认拷贝构造函数的机制一样喵显式重载的话也要将全部都显式调用喵。struct person { person(string s) :_s(s) { } person operator(const person x) { if (this ! x) { _s x._s; } return *this; } string _s; }; struct student:person { student(const string s1,const string s2,int n) :person(s1) ,_s1(s2) ,_num(n) { } student(const student x) :person(x) ,_num(x._num) ,_s1(x._s1) { } student operator(const student x) { if (this ! x) { //调用person的重载要确认作用域喵 //因为student和person的operator名字相同构成隐藏喵 person::operator(x); _num x._num; _s x._s; } return *this; } int _num; string _s1; }; int main() { student a(galgame, love, 100); student b(a); cout b._s b._s1 b._num endl; student c(yinli, miaomiao,200); b c; cout b._s b._s1 b._num endl; return 0; }4.最后是析构喵析构中所有类的析构函数的析构名都会被处理成destructor()函数名因此子类和父类的析构函数也构成隐藏关系喵。正常情况下都是不用我们写析构的喵。编译器自己生成的就会调用父类和类类型成员的析构了喵。值得注意的一点是无论显式调用的析构中是否有调用父类的析构函数编译器在析构函数结束前依旧会再调用一次父类和类类型成员的析构喵。如下面喵C是一个类类型成员喵上面的这种代码就代表了会分别调用两次person和C的析构函数喵。因此析构函数是真的不用我们去调用父类和类类型成员的析构的喵。七三个小知识点喵1.让父类无法被子类继承的方式喵(1)让父类的构造函数私有化喵struct A { private: A() { } int _a 30; }; struct B : A { int _b 80; }; int main() { B a; return 0; }会报错喵不创建a变量就不会error了喵(2)给父类的名字后面加个final喵struct A final { A() { } int _a 30; }; struct B : A { int _b 80; }; int main() { //这种不创建变量也会error喵 //B a; return 0; }2.友元关系不能被继承struct A { friend void func(); private: int _a 20; int _b 40; }; struct B :A { private: int _c 30; }; void func() { A a; B c; cout can use it endl; cout a._a a._b endl; //上面是可以的但_c是B的变量func是A的友元但不是B的. cout c._c endl; }3.父类中的static成员子类中共用struct A { static int a; int b 40; }; struct B : A { int b 30; }; int A::a 10; int main() { A a; B b; //一个值 cout A::a endl; cout B::a endl; //一个地址 cout a.a endl; cout b.a endl; //这里说明这是两个变量 //且常规变量是不共用的喵 cout a.b endl; cout b.b endl; return 0; }八单继承与多继承喵单继承一个子类只有一个直接父类喵。多继承一个子类有多个直接父类喵。struct person { string s; }; struct teacher { string h; }; //单继承 struct student :person { string d; }; //单继承 struct man :student { string l; }; //多继承 struct assistant :public person, public teacher { string c; };菱形继承喵菱形继承的问题喵数据冗杂和二义性喵数据冗杂animal有两份喵二义性调用时不知道animal是哪一个喵struct person { string _name fs; }; struct student:person { string _big op; }; struct teacher :person { string _small jj; }; struct assistant :student, teacher { string _galgame oo; }; int main() { assistant a; cout a._name endl; return 0; }a._name的调用不明确喵。只能指定作用域调用喵。最经典的菱形调用解决方法喵虚继承 virtual(写在具有二义性的地方)此时student和teacher会共用一个person了。虚继承两边都要写喵不然会另一个virtual就是无效的喵。只要有二义性和数据冗杂的都叫菱形继承喵。总之一定不要设计出菱形继承啊喵。补充道调试下assistant会先走person的构造(越早的类越早调喵)在student和teacher中的person构造编译器会直接跳过喵。xiamian下面来一道指针偏离的问题喵。class Base1 { public: int _b1; }; class Base2 { public: int _b2; }; class Derive : public Base1, public Base2 { public: int _d; }; int main() { Derive d; Base1* p1 d; Base2* p2 d; Derive* p3 d; return 0; }因此p1p3!p2喵。九继承(is_a)与组合(has_a)喵//这是继承 struct stack :public list {}; //这是组合 struct stack { list it; };is_a和has_a就是继承和组合的别名喵。耦合度几者之间的关系密切程度喵。组合就是车有轮胎强调有的关系喵(一般耦合度较低喵)。继承就是galgame是视觉小说游戏强调是的关系(一般耦合度较高喵)。可以is_a也可以has_a时优先使用has_a喵。补充喵模板特化中如int max(int a,int b) int maxint(int a,int b) int main(){ max(1020); maxint(10,20); return 0;}调用中没有模板时优先调用常规函数有模板变量申明时一定是走的实例化喵。总结写不动了喵燃尽了喵。继承的访问限定符的类模板的切片的作用域的默认函数的单多继承喵。