c++中的类(class)-----笔记(类继承)

1,派生类继承了基类的所有成员函数和数据成员(构造函数、析构函数和操作符重载函数外)。

2,当不指明继承方式时,默认为私有继承。

3,基类的私有成员仅在基类中可见,在派生类中是不可见的。基类的私有成员可以由派生类继承,但在派生类中不可见。尽管在派生类中不能直接访问基类的私有成员,但可以通过间接的方式进行访问(设置公有成员访问函数)。

4,改变访问限制:通过使用 using 声明可以改变成员在派生类中的访问限制。

 1 class BC {
 2     public:
 3         void set_x(float a) { x = a;}
 4     private:
 5         float x;
 6 }; 
 7 
 8 class DC : public BC { 
 9     public:
10         void set_y(float b) { y = b;}
11     private:
12         using BC::set_x;  // 只写函数名 
13         float y;
14 };
using 使用示例

5,名字隐藏:如果派生类添加了一个数据成员,而该成员与基类中的某个数据成员同名,新的数据成员就隐藏了继承而来的同名成员,同理,如果派生类添加了与基类的某个成员函数同名的函数,则该函数就隐藏了基类中的同名函数。

 1 class BC {
 2     public:
 3         void h(float);
 4 }; 
 5 
 6 class DC : public BC {
 7     public:
 8         void h(char []);
 9 };
10 
11 int main() {
12     DC d1;
13     d1.h("Boffo!");  // DC::h,not BC::h
14     //d1.h(707.7);  //  *****ERROR:DC:h hides BC::h
15     d1.BC::h(707.7);  // OK: invokes BC::h
16     
17     return 0;
18 }  
名字隐藏

6,保护成员:在没有继承的情况下,保护成员和私有成员类似(类的对象不能直接访问保护成员)。保护成员在派生类中是可见的,仅在类层次结构中可见。一般避免将数据成员设计成保护类型,设计一个用来进行存取访问的保护成员函数,通常将这种类型的成员函数称为访问函数

7,派生类不能访问一个基类对象的保护成员,这是因为基类对象属于基类,不属于派生类。

 1 class BC {
 2     protected:
 3         int get_w() const;
 4         //...
 5 };
 6 
 7 class DC : public BC {
 8     void get_val() const { return get_w();}
 9     void base_w( const BC& b) const { cout<<b.get_w()<<endl; }  // ERROR
10 };
基类对象错误使用示例

8,继承机制下的构造函数:当创建一个派生类对象时,基类的构造函数被自动调用,用来对派生类对象中的基类部分进行初始化,并完成其它一些事务。如果派生类定义了自己的构造函数,则由该构造函数负责对象中“派生类添加部分”的初始化工作。

9,有时候基类的构造函数的功能对派生类而言已经足够,这时候派生类不必自行设计构造函数,否则派生类必须定义自己的构造函数。可以在派生类的构造函数中调用其基类的构造函数(前提是基类拥有构造函数)。

10,在一个层次很深的类层次结构中,创建一个派生类对象将导致派生链中的所有类的构造函数被逐一调用,这是一个多米诺骨牌效应。构造函数的函数体将按照自顶向下(依照继承层次)的次序依次执行。

 1 class Animal {
 2     public:
 3         Animal() { species = "Animal";}
 4         Animal( const char* s) { species = s;}
 5     private:
 6         string species;
 7 }; 
 8 
 9 class Primate : public Animal {
10     public:
11         Primate() : Animal("primate") { }
12         Primate(int n) : Animal("primate") { heart_cham = n;}
13     private:
14         int heart_cham;
15 };
16 
17 class Human : public Primate {
18     public:
19         Human() : Primate() { }
20         Human(int c) : Primate(c) { }
21 };
类继承示例

11,派生类构造函数的规则:如果基类拥有构造函数但没有默认构造函数,那么派生类的构造函数必须显式地调用基类的某个构造函数。一般建议为每个基类都设计一个默认构造函数。

 1 class BC {
 2     public:
 3         BC(int a) { x = a;y = 999;}
 4         BC(int a,int b) { x = a;y = b;}
 5     private:
 6         int x;
 7         int y;
 8 };
 9 
10 class DC : public BC {
11     public:
12         DC(int n) { z = n;}  // ERROR: DC(int) must explicitly invoke a BC constructor
13     private:
14         int z;
15 };
派生类构造函数规则错误示例

总结:(a)若 DC 有构造函数而 BC 没有,当创建 DC 类的对象时,DC 的相应构造函数被自动调用。

   (b)若 DC 没有构造函数而 BC 有,则 BC 必须拥有默认构造函数,只有这样,当创建 DC 类的对象时,才会自动执行BC 的默认构造函数。

   (c)若 DC 有构造函数,而且 BC 有默认构造函数,则创建 DC 类的对象时,BC 的默认构造函数会自动执行,除非当前被调用的派生类构造函数在其初始化段中显式地调用了 BC 的非默认构造函数。

   (d)若 DC 和 BC 都有构造函数,但 BC 没有默认构造函数,则 DC 的每个默认构造函数必须在其初始化段中显式地调用 BC 的某个构造函数。只有这样,当创建 DC 类的对象时,BC 的构造函数才能获得执行机会。

12,继承机制下的析构函数:析构函数按照派生类到基类的次序执行,因此,析构函数的执行次序和构造函数的执行次序时相反的。由于每个类至多只有一个析构函数,因此对析构函数的调用不会产生二义性,这样在析构函数中不必显式地调用其他析构函数。

 1 class BC {
 2     public:
 3         BC() { cout<<"BC's constructor"<<endl;}
 4         ~BC() { cout<<"BC's destructor"<<endl;}
 5 };
 6 
 7 class DC : public BC {
 8     public:
 9         DC() { cout<<"DC's constructor"<<endl;}
10         ~DC() { cout<<"DC's destructor"<<endl;}
11 };
12 
13 
14 int main() {
15     DC d;
16     
17     return 0;
18 } 
19 
20 // out
21 //BC's constructor
22 //DC's constructor
23 //DC's destructor
24 //BC's destructor
析构函数使用示例

13,多继承:在多继承中,一个派生类可以有多个基类,构造的层次结构是图。派生类是其所有基类的组合体。

14,继承和访问规则:使用多继承机制,将增加命名冲突出现的可能性,表现形式有两种:(a)派生类和某个基类之间发生命名冲突;(b)基类与基类之间发生命名冲突。(在单继承中称为名字隐藏),通过使用域解析符来解决。

 1 class BC1 {
 2     private:
 3         int x;
 4     public:
 5         void set_x(int a) { x = a;}
 6 };
 7 
 8 class BC2 {
 9     private:
10         int x;
11     public:
12         void set_x(int a) { x = a;}
13 };
14 
15 class DC : public BC1,public BC2 {
16     private:
17         int x;
18     public:
19         void set_x(int a) { x = a;}
20 };
21 
22 int main() {
23     DC d;
24     d.set_x(999); // local set_x
25     d.BC1::set_x(111);
26     d.BC2::set_x(222);
27     return 0;
28 } 
命名冲突示例

15,虚基类:解决派生类从同一个间接基类继承了多次(获取了相同的数据成员 x 多次),用虚基类来解决。将 DC1 和 DC2 说明为 z 的虚基类,就是要求 DC1 和 DC2 仅将同名数据成员的一份拷贝发放到 z 当中,而不管 DC1 和 DC2 从共同的祖先获得了多少个同名的数据成员。

 1 class BC {
 2     int x;
 3     //...
 4 };
 5 
 6 class DC1 : virtual public BC {  // 改 DC1 为虚基类 
 7     //...
 8 };
 9 
10 class DC2 : virtual public BC {  // 改 DC2 为虚基类 
11     //...
12 };
13 
14 class z : public DC1,public DC2 {
15     //...
16 };
虚基类示例

16,保护继承:(a)基类的所有成员在派生类中是保护成员。

        (b)基类中的保护成员在派生类中是保护成员。

        (c) 基类中的所有私有成员仅在基类中可见。

17,私有继承:

        (a)基类的所有成员在派生类中是私有的。

        (b)基类的保护成员在派生类中是私有的。

        (c) 基类的私有成员仅在基类中可见。

 

posted on 2019-03-02 20:42  爱笑的张飞  阅读(2052)  评论(0编辑  收藏  举报

导航