一、继承限定词(不写的话默认是private)
class X : public Y
public: 父类怎样,子类怎样
protected:父类的public在子类中变成protected
private: 父类的pubic和protected在子类中都变成private
总结:(注意区分 子类中,子类对象 这两个概念)
(1) 访问说明符的作用是控制子类用户(即子类的对象,不是子类内部)对于父类成员的访问权限,父类中比继承限定词高级的限定词语的等级将会在子类中会降为和继承限定词一样。
(2) 不论何种继承方式,父类的protected成员在子类中(即子类内部,不是子类对象)可以直接访问,但是外界定义的子类对象不能访问。
(3) 不论何种继承方式,父类的private成员不论是在子类中,还是对于子类的对象都不可以访问。
(4) 不论何种继承方式,父类的protected成员在子类中都是可见的,父类的private成员在子类中都是不可见的,但是子类还是继承了父类的private成员。
1 #include2 3 using namespace std; 4 5 class Father 6 { 7 private: 8 int a; 9 float b;10 11 };12 13 class Son : private Father14 {15 16 };17 18 int main()19 {20 cout << sizeof(Son) << endl; //输出结果为8,说明子类继承了父类的private成员21 return 0;22 }
二、使用using修改父类个别成员在子类中的可访问性(不能对父类的private成员使用using声明,即不能修改父类的private成员在子类中的访问权限)
using声明语句中名字的访问权限由该using声明语句之前的访问说明符来决定。子类只能为那些它可以访问的名字提供using声明。(所以在子类中不能对父类的private成员使用using声明,即在子类中不能使用using声明修改父类private成员在子类中的访问权限)。
using声明不受继承方式的影响。
1 class Father 2 { 3 public: 4 int size() const { return n;} 5 protected: 6 int n = 5; 7 private: 8 int m = 3; 9 10 };11 12 class Son : private Father //using声明不受继承方式的影响13 {14 private:15 using Father::size; //正确:使用using声明将继承自Father类中public的size()成员修改为了private(在父类中size()是public的,在子类中size()被修改为了private)16 public:17 using Father::n; //正确:使用using声明将Father类中protected的n修改为了public(在父类中n是protected的,在子类中n被修改为了public)18 using Father::m; //错误:不能使用using修改父类private成员在子类中的访问权限!19 };
三、构造函数和析构函数的调用顺序:
1、子类构建对象时,先调用父类的构造函数,再调用子类的构造函数。
2、子类的对象销毁时,先调用子类的析构函数,再调用父类的析构函数。 ( 即调用子类的析构函数时,一定会在之后同时调用父类的析构函数。)
3、可以在子类构造函数的初始化列表里面显式的调用父类的带参构造函数。
(1)
1 #include2 3 using namespace std; 4 5 class GrandFarther 6 { 7 public: 8 GrandFarther() 9 {10 cout << "GrandFarther();" << endl;11 }12 ~GrandFarther()13 {14 cout << "~GrandFarther();" << endl;15 }16 };17 class Father : public GrandFarther18 {19 public:20 Father()21 {22 cout << "Father();" << endl;23 }24 ~Father()25 {26 cout << "~Father();" << endl;27 }28 };29 class Son : public Father30 {31 public:32 Son()33 {34 cout << "Son();" << endl;35 }36 ~Son()37 {38 cout << "~Son();" << endl;39 }40 };41 42 int main()43 {44 Son xiaoming;45 return 0;46 }
输出为:
GrandFarther();
Farther();
Son();
~Son();
~Farther();
~GrandFarther();
(2)
1 #include2 3 using namespace std; 4 5 class father 6 { 7 public: 8 father() 9 {10 cout << "father()" << endl;11 }12 father(int a, int b)13 {14 printf("father(%d,%d)\n", a, b);15 }16 };17 18 class son : public father19 {20 public:21 son() : father(1,2) //显式调用father(int a,int b)22 {23 cout << "son()" << endl;24 }25 };26 int main()27 {28 son xiaoming;29 30 return 0;31 }
输出为:
father(1,2)
son()
(3) 调用子类的析构函数时,一定会在之后同时调用父类的析构函数。
1 #include2 3 using namespace std; 4 5 class father 6 { 7 public: 8 virtual ~father() 9 {10 cout << "~father()" << endl;11 }12 };13 14 class son : public father15 {16 public:17 ~son()18 {19 cout << "~son()" << endl;20 }21 };22 int main()23 {24 son* p1 = new son;25 delete p1; //delete先调用~son(),再调用~father()。26 27 father* p2 = new son;28 delete p2; //由于父类的析构函数是virtual的,所以此处delete销毁的是子类对象,故先调用~son(),再调用~father()。29 30 return 0;31 }
四、virtual关键字 (主要是针对父类的指针指向子类的对象所产生的相关问题) 具体请参考:https://www.cnblogs.com/FengZeng666/p/9341861.html
(1) 构造函数不能是virtual的。
(2) static成员函数不能动态联编,所以静态函数不能是virtual的。
(3) 友元函数不能是virtual的。因为C++不支持友元函数的继承,对于没有继承特性的函数没有虚函数的说法。友元函数不属于类的成员函数,不能被继承。
(4) 函数前加virtual 关键字的作用:根据对象的类型,调用相应的函数(即保证调用的函数与对象类型一致)
1 #include2 3 using namespace std; 4 5 class father 6 { 7 public: 8 void show() 9 {10 cout << "show() in father" << endl;11 }12 13 };14 15 class son : public father16 {17 public:18 void show()19 {20 cout << "show() in son" << endl;21 }22 };23 int main()24 {25 son s;26 father* p1 = &s; 27 father* p2 = new son;28 p1->show(); //当father类中的show()前没加virtual关键字时,调用的是father里面的show();当father类中的show()前加了virtual关键字时,调用的则是son类里面的show() 29 p2->show(); //同上 30 31 return 0; 32 }
(5)当一个类被继承时,必须在父类的析构函数前加virtual,以避免潜在的问题。(父类的析构函数前加了virtual之后,子类的析构函数默认也是virtual的)
例如:
1 #include2 3 using namespace std; 4 5 class father 6 { 7 public: 8 virtual ~father() 9 {10 cout << "~father()" << endl;11 }12 };13 14 class son : public father15 {16 public:17 ~son()18 {19 cout << "~son()" << endl;20 }21 };22 int main()23 {24 father* p1 = new son;25 delete p1;26 27 return 0;28 }
当~father前面没有加virtual时,输出为~father(),说明delete调用的是father类的析构函数,然而我们指向的是一个son类型的对象,我们的本意是要其调用son的析构函数,这显然与我们的本意不符。所以加上~father()函数前virtual时,输出为 ~son() 回车 ~father() ,即delete调用了子类的析构函数,销毁的是一个son的对象,符合本意。又因为销毁子类对象时,会在之后同时调用父类的析构函数,所以之后也会输出~father()
五、名字查找优先于类型检查
(1)声明在内层作用域的函数并不会重载声明在外层作用域的函数。因此,子类中与父类同名的函数并不会重载其在父类中的函数,即,子类将在其作用域内隐藏掉其父类中的同名函数成员(并不是重载),即使形参列表不一致,父类成员也依然会被隐藏掉。
1 #include2 3 using namespace std; 4 5 class Father 6 { 7 public: 8 void fun() 9 {10 cout << "fun()" << endl;11 }12 };13 14 class Son : public Father15 {16 public:17 void fun(int)18 {19 cout << "fun(int)" << endl;20 }21 };22 23 int main()24 {25 Father fa;26 Son so;27 fa.fun(); //调用父类中的fun()28 so.fun(10); //调用子类中的fun(int)29 so.fun(); //错误:父类中的fun()被子类的fun()隐藏了(所以并不是重载的关系)30 so.Father::fun(); //正确:通过子类对象调用了父类中的fun():使用作用域运算符31 return 0;32 33 }