(74)c++再回顾一继承和派生

一:继承和派生ios

0、默认构造函数即不带参数的构造函数或者是系统自动生成的构造函数。每个类的构造函数能够有多个,可是析构函数只能有一个。c++

一、采用公用public继承方式,则基类的公有成员变量和成员函数的属性继承到派生类后不发生变化。例如book类的公有的setprice和settitle成员函数继承到codingbook类后,这两个成员变量的属性仍将是public属性。若是在继承过程当中不指名继承方式时,编译器系统会默认继承方式为private或protected属性。编程

二、派生类在继承了父类的全部成员函数和成员变量后,是不能直接访问基类的私有成员函数和成员变量的。函数

三、从基类派生出派生类,派生类继承基类的继承方式有三种:public、protected和private。在未指定的状况下编译器会默认继承方式为protected或private方式。
spa

1) public继承方式

    • 基类中全部public成员在派生类中为public属性,能够访问
    • 基类中全部protected成员在派生类中为protected属性,不能够访问
    • 基类中全部private成员在派生类中不可访问

2) protected继承方式

  • 基类中的全部public成员在派生类中为protected属性,不能够访问
  • 基类中的全部protected成员在派生类中为protected属性,不能够访问
  • 基类中的全部private成员在派生类中仍然不可访问

3) private继承方式

  • 基类中的全部public成员在派生类中均为private属性;不能够访问
  • 基类中的全部protected成员在派生类中均为private属性;不能够访问
  • 基类中的全部private成员在派生类中均不可访问

四、使用using声明能够改变基类成员在派生类中的访问属性。咱们知道基类的公有成员通过公有继承,在派生类中其属性为public的,可是经过using 声明,咱们能够将其改成private或protected属性。设计

五、若是派生类中新增一个成员变量,该成员变量与基类中的成员变量同名,则新增的成员变量就会遮蔽从基类中继承过来的成员变量。同理,若是派生类中新增的成员函数与基类中的成员函数同名,则该新增的成员函数就会遮蔽从基类中继承过来的成员函数。指针

  1. #include<iostream>
  2. #include<stdio.h>
  3. //c++继承和派生
  4. using namespace std;
  5. class A{
  6. public:
  7. A(){
  8. a=3;
  9. c=100;
  10. d=12;
  11. }
  12. int c;
  13. void Display(){
  14. cout<<"A Display"<<endl;
  15. }
  16. private:
  17. int a;
  18. protected:
  19. int d;
  20. };
  21. class B:public A{
  22. public:
  23. B(){c=917;b=4;}
  24. int c;
  25. void Display(){
  26. cout<<"B Display"<<endl;
  27. }
  28. private:
  29. int b;
  30. };
  31. int main(){
  32. B*p=new B;
  33. cout<<p->c<<endl;
  34. p->Display();
  35. p->A::Display(); //若是不想被遮蔽的书写方式
  36. system("pause");
  37. return 0;
  38. }

从上例中,咱们能够看出被遮蔽了的基类的成员变量或成员函数并不是是没有继承过来,而仅仅是被派生类的同名成员变量和成员函数给遮蔽了,调用的时候须要用到类名加上域解析操做符。orm

六、假设类C继承自类B,类B继承自类A。那么类C中的除了可以继承B类的成员函数和成员变量外,一样也能继承B类继承自A类的全部成员。换言之,类C能够继承来自类A的全部成员。所以继承既能够是直接继承,也能够是间接继承。对象

七、继承机制下的构造函数:派生类一样有构造函数。当咱们建立一个派生类对象的时候,基类构造函数将会被自动调用,用于初始化派生类从基类中继承过来的成员变量。而派生类中新增的成员变量则须要从新定义构造函数用于初始化了。不管是对象指针的方式或者是直接建立对象的方式都会先调用基类的构造函数,而后调用派生类的构造函数。blog

当咱们建立派生类对象时,先由派生类构造函数调用基类构造函数,而后再执行派生类构造函数函数体中的内容,也就是说先执行基类构造函数,而后再去执行派生类构造函数。若是继承关系有好几层的话,例如A类派生出B类,B类派生出C类,则建立C类对象时,构造函数的执行顺序则为A的构造函数,其次是B的构造函数,最后是C类的构造函数。构造函数的调用顺序是按照继承的层次,自顶向下,从基类再到派生类的。

八、派生类和基类构造函数的调用规则:基类默认构造函数-----》基类带参构造函数-----》派生类默认构造函数-------》派生类带参构造函数

九、派生类构造函数能够自动调用基类的默认构造函数,可是前提是默认构造函数必须存在。一般状况下,默认构造函数系统会自动生成的,可是若是在基类中,咱们本身定义了一个带参数的构造函数,这个时候,系统是不会为基类自动生成默认构造函数的,这个时候派生类则没法自动调用基类的默认构造函数了,由于基类根本就不存在默认构造函数。遇到这种状况有两种解决方案:其一,在基类中定义一个默认构造函数(不带参数的构造函数),例如上一节中的例2;其二,派生类中的每个构造函数都显式的调用基类中的带参构造函数,显示调用方法:

咱们还建议在设计类的时候为每个类设计一个默认构造函数,毕竟默认构造函数并不会妨碍构造函数的显式调用。一般咱们还会遇到这样一种状况,派生类中并未显式定义构造函数,这个时候派生类中只有系统自动生成的默认构造函数,如此一来,若是咱们不为基类设计一个默认构造函数,则程序就会编译出错。这种错误很玄妙,若是不当心还真是难以发现。为了不这种状况的发生,咱们建议为每个类设计一个默认构造函数。

总的来讲,在建立派生类对象时,必须显式或隐式地调用基类的某一个构造函数,这一点很是重要。固然被调用的基类的构造函数能够是带参构造函数,也能够是默认构造函数。

十、建立派生类对象时构造函数的调用顺序是按照继承顺序,先执行基类构造函数,而后再执行派生类的构造函数。可是对于析构函数,其调用顺序是正好相反的,即先执行派生类的构造函数,而后再执行基类的构造函数。构造函数的执行顺序是按照继承顺序自顶向下的,从基类到派生类,而析构函数的执行顺序是按照继承顺序自下向上,从派生类到基类。由于每个类中最多只能有一个析构函数,所以调用的时候并不会出现二义性,所以析构函数不须要显式的调用。
十一、c++多继承:

在C++中一个派生类中容许有两个及以上的基类,咱们称这种状况为多继承。单继承中派生类是对基类的特例化,例如前面中编程类书籍是书籍中的特例。而多继承中,派生类是全部基类的一种组合。在多继承中,派生类继承了全部基类中的全部成员变量和成员函数,这些继承过来的成员变量及成员函数其访问规则与单继承是相同的。使用多继承能够描述事物之间的组合关系,可是如此一来也可能会增长命名冲突的可能性,冲突可能颇有可能发生在基类与基类之间,基类与派生类之间。命名冲突是必需要解决的问题。为了解决命名冲突问题咱们只能采用域解析操做符来区分具体所调用的类中的成员函数,例如:

十二、c++虚基类(虚继承):

在多继承时很容易产生命名冲突问题,若是咱们很当心地将全部类中的成员变量及成员函数都命名为不一样的名字时,命名冲突依然有可能发生,好比很是经典的菱形继承层次。类A派生出类B和类C,类D继承自类B和类C,这个时候类A中的成员变量和成员函数继承到类D中变成了两份,一份来自A派生B而后派生D这一路,另外一份来自A派生C而后派生D这一条路。


例1:

  1. class A
  2. {
  3. public:
  4. void setx(int a){x = a;}
  5. int getx(){return x;}
  6. private:
  7. int x;
  8. };
  9. class B: public A
  10. {
  11. public:
  12. void sety(int a){y = a;}
  13. int gety(){return y;}
  14. private:
  15. int y;
  16. };
  17. class C: public A
  18. {
  19. public:
  20. void setz(int a){z = a;}
  21. int getz(){return z;}
  22. private:
  23. int z;
  24. };
  25. class D: public B, public C
  26. {
  27. //......
  28. };

本例即为典型的菱形继承结构,类A中的成员变量及成员函数继承到类D中均会产生两份,这样的命名冲突很是的棘手,经过域解析操做符已经没法分清具体的变量了。为此,C++提供了虚继承这一方式解决命名冲突问题。虚继承只须要在继承属性前加上virtual关键字。

例2:

  1. #include <iostream>
  2. using namespace std;
  3. class A
  4. {
  5. public:
  6. void setx(int a){x = a;}
  7. int getx(){return x;}
  8. private:
  9. int x;
  10. };
  11. class B: virtual public A
  12. {
  13. public:
  14. void sety(int a){y = a;}
  15. int gety(){return y;}
  16. private:
  17. int y;
  18. };
  19. class C: virtual public A
  20. {
  21. public:
  22. void setz(int a){z = a;}
  23. int getz(){return z;}
  24. private:
  25. int z;
  26. };
  27. class D: public B, public C
  28. {
  29. //......
  30. };
  31. int main()
  32. {
  33. D test;
  34. test.setx(10);
  35. cout<<test.getx()<<endl;
  36. return 0;
  37. }

在本例中,类B和类C都是继承类A都是虚继承,如此操做以后,类D只会获得一份来自类A的数据。在本例的主函数中,定义了类D的对象test,而后经过该对象调用从类A间接继承来的setx和getx成员函数,由于B和C继承自类A采用的是虚继承,故经过D调用setx和getx不会有命名冲突问题,由于D类只获得了一份A的数据。

相关文章
相关标签/搜索