c++详细学习——继承


通常讲父类(parrent)-子类(child)、基类(base)-派生类(derived)和超类(super)-子类(sub)

1 最基础的写法

 以下例子为最基本的写法,默认构造

 1 enum Gender {
 2     MALE,
 3     FEMALE,
 4 };
 5 
 6 class Person
 7 {
 8 private:
 9     string name;
10     Gender gend;
11     int age;
12 public:
13     Person():name(""), gend(Gender::MALE), age(18)
14     {
15         cout << "Person::Person()" << endl;
16     }
17     ~Person()
18     {
19         cout << "Person::~Person()" << endl;
20     }
21 };
22 
23 class Student : public Person
24 {
25 private:
26     int studentId;
27     int score;
28 public:
29     Student():studentId(10000), score(100)
30     {
31         cout << "Student::Student()" << endl;
32     }
33     ~Student()
34     {
35         cout << "Student::~Student()" << endl;
36     }
37 };

 测试:

1 void TestAccess()
2 {
3     Student stu;
4 }

从运行结果来看,声明一个Student的对象之后,依次执行的是:Person的构造函数 --> Student的构造函数 --> Student的析构函数 --> Person的析构函数

2 访问权限

 2.1 public

父类的所有public成员变量和函数都可以被子类访问、操作

Example

例子中父类Person的成员变量int age放在public中,不提供操作函数,测试函数中申明子类Student的对象后直接对age进行写和读,编译运行ok

 1 enum Gender {
 2     MALE,
 3     FEMALE,
 4 };
 5 
 6 class Person
 7 {
 8 private:
 9     string name;
10     Gender gend;
11 public:
12     Person():name(""), gend(Gender::MALE), age(18)
13     {
14         cout << "Person::Person()" << endl;
15     }
16     ~Person()
17     {
18         cout << "Person::~Person()" << endl;
19     }
20 
21     void SetName(const string name_)
22     {
23         name = name_;
24     }
25     string GetName() const
26     {
27         return name;
28     }
29 
30     int age;
31 
32     void SetGender(const Gender gend_)
33     {
34         gend = gend_;
35     }
36     Gender GetGender() const
37     {
38         return gend;
39     }
40 };
41 
42 class Student : public Person
43 {
44 private:
45     int studentId;
46     int score;
47 public:
48     Student():studentId(10000), score(100)
49     {
50         cout << "Student::Student()" << endl;
51     }
52     ~Student()
53     {
54         cout << "Student::~Student()" << endl;
55     }
56 
57     void SetStudentId(const int id)
58     {
59         studentId = id;
60     }
61     int GetStudentId() const
62     {
63         return studentId;
64     }
65 
66     void SetScore(const int score_)
67     {
68         score = score_;
69     }
70     int GetScore() const
71     {
72         return score;
73     }
74 };

测试函数:

 1 void TestAccess()
 2 {
 3     Student stu;
 4     stu.SetName("Tom");
 5     stu.SetGender(Gender::FEMALE);
 6     stu.age = 20; /* 直接操作成员变量 */
 7     stu.SetStudentId(10001);
 8     stu.SetScore(88);
 9 
10     cout << "name\t" << "gender\t" << "age\t" << "syudent ID\t" << "score" << endl;
11     cout << stu.GetName() << "\t" << stu.GetGender() << "\t" << stu.age << "\t"
12         << stu.GetStudentId() << "\t\t" << stu.GetScore() << endl;
13 }

运行结果ok:

2.2 private

子类拥有父类的所有private成员变量,可以通过public中父类提供的操作函数进行访问,但是没有直接操作和访问权限

Example

例子中父类Person的private成员变量int age,不提供操作函数,测试函数中申明子类Student的对象后直接对age进行写和读,编译error

 1 enum Gender {
 2     MALE,
 3     FEMALE,
 4 };
 5 
 6 class Person
 7 {
 8 private:
 9     string name;
10     Gender gend;
11     int age;
12 public:
13     Person():name(""), gend(Gender::MALE), age(18)
14     {
15         cout << "Person::Person()" << endl;
16     }
17     ~Person()
18     {
19         cout << "Person::~Person()" << endl;
20     }
21 
22     void SetName(const string name_)
23     {
24         name = name_;
25     }
26     string GetName() const
27     {
28         return name;
29     }
30 
31     void SetGender(const Gender gend_)
32     {
33         gend = gend_;
34     }
35     Gender GetGender() const
36     {
37         return gend;
38     }
39 };
40 
41 class Student : public Person
42 {
43 private:
44     int studentId;
45     int score;
46 public:
47     Student():studentId(10000), score(100)
48     {
49         cout << "Student::Student()" << endl;
50     }
51     ~Student()
52     {
53         cout << "Student::~Student()" << endl;
54     }
55 
56     void SetStudentId(const int id)
57     {
58         studentId = id;
59     }
60     int GetStudentId() const
61     {
62         return studentId;
63     }
64 
65     void SetScore(const int score_)
66     {
67         score = score_;
68     }
69     int GetScore() const
70     {
71         return score;
72     }
73 };

测试函数:

 1 void TestAccess()
 2 {
 3     Student stu;
 4     stu.SetName("Tom");
 5     stu.SetGender(Gender::FEMALE);
 6     stu.age = 20; /* 直接操作成员变量 */
 7     stu.SetStudentId(10001);
 8     stu.SetScore(88);
 9 
10     cout << "name\t" << "gender\t" << "age\t" << "syudent ID\t" << "score" << endl;
11     cout << stu.GetName() << "\t" << stu.GetGender() << "\t" << stu.age << "\t"
12         << stu.GetStudentId() << "\t\t" << stu.GetScore() << endl;
13 }

编译结果error

2.3 protected

 父类protected成员变量和函数,子类可以当父类的private变量使用,但父类的对象不能不能使用

Example

例子中父类Person的protected成员函数PrintBaseInfo(),测试函数中Person的对象直接使用,编译error。但子类Student可以把它当父类的private成员函数一样使用。

 1 enum Gender {
 2     MALE,
 3     FEMALE,
 4 };
 5 
 6 class Person
 7 {
 8 private:
 9     string name;
10     Gender gend;
11     int age;
12 protected:
13     void PrintBaseInfo()
14     {
15         cout << "Person::PrintBaseInfo()\t";
16         cout << "name: " << name << "\tgend: " << gend << "\tage: " << age << endl;
17     }
18 public:
19     Person():name(""), gend(Gender::MALE), age(18)
20     {
21         cout << "Person::Person()" << endl;
22     }
23     ~Person()
24     {
25         cout << "Person::~Person()" << endl;
26     }
27 
28     void SetName(const string name_)
29     {
30         name = name_;
31     }
32     string GetName() const
33     {
34         return name;
35     }
36 
37     void SetGender(const Gender gend_)
38     {
39         gend = gend_;
40     }
41     Gender GetGender() const
42     {
43         return gend;
44     }
45 
46     void SetAge(const int age_)
47     {
48         age = age_;
49     }
50     int GetAge() const
51     {
52         return age;
53     }
54 };
55 
56 class Student : public Person
57 {
58 private:
59     int studentId;
60     int score;
61 public:
62     Student():studentId(10000), score(100)
63     {
64         cout << "Student::Student()" << endl;
65     }
66     ~Student()
67     {
68         cout << "Student::~Student()" << endl;
69     }
70 
71     void SetStudentId(const int id)
72     {
73         studentId = id;
74     }
75     int GetStudentId() const
76     {
77         return studentId;
78     }
79 
80     void SetScore(const int score_)
81     {
82         score = score_;
83     }
84     int GetScore() const
85     {
86         return score;
87     }
88 
89     void Print()
90     {
91         PrintBaseInfo(); /* 子类使用父类的protected成员函数, ok */
92     }
93 };

测试函数:

 1 void TestAccess()
 2 {
 3     Person per;
 4     // per.PrintBaseInfo(); /* error */
 5 
 6     Student stu;
 7     stu.SetName("Tom");
 8     stu.SetGender(Gender::FEMALE);
 9     stu.SetAge(24);
10     stu.SetStudentId(10001);
11     stu.SetScore(88);
12     stu.Print();
13 
14     cout << "name\t" << "gender\t" << "age\t" << "syudent ID\t" << "score" << endl;
15     cout << stu.GetName() << "\t" << stu.GetGender() << "\t" << stu.GetAge() << "\t"
16         << stu.GetStudentId() << "\t\t" << stu.GetScore() << endl;
17 }

执行结果:

3 构造和析构

在第1章中已经介绍,依次执行的是:Person的构造函数 --> Student的构造函数 --> Student的析构函数 --> Person的析构函数

子类对父类的初始化必须放在子类构造函数的初始化列表中,这是初始化父类的唯一办法

 1 enum Gender {
 2     MALE,
 3     FEMALE,
 4 };
 5 
 6 class Person
 7 {
 8 private:
 9     string name;
10     Gender gend;
11     int age;
12 public:
13     Person(const string &_name, const Gender _gend, const int _age) : name(_name), gend(_gend), age(_age)
14     {
15         cout << "Person::Person()" << endl;
16     }
17     ~Person()
18     {
19         cout << "Person::~Person()" << endl;
20     }
21 };
22 
23 class Student : public Person
24 {
25 private:
26     int studentId;
27     int score;
28 public:
29     Student(const int id, const int _score) :  Person("", Gender::MALE, 18), studentId(id), score(_score)
30     {
31         cout << "Student::Student()" << endl;
32     }
33     ~Student()
34     {
35         cout << "Student::~Student()" << endl;
36     }
37 };

4 继承与重载

4.1 重载(overload)

 在类中有多个函数类型和函数名相同,但参数列表不同的成员函数时,各个同名的函数之间形成重载,调用时根据调用者的参数去匹配这些重载函数

Example

Person类中4个Print之间构成重载的关系,Person的对象调用时根据传的实参参数类型或个数不同,执行不同的Print()。

 1 enum Gender {
 2     MALE,
 3     FEMALE,
 4 };
 5 
 6 class Person
 7 {
 8 private:
 9     string name;
10     Gender gend;
11     int age;
12 public:
13     Person(const string &_name, const Gender _gend, const int _age) : name(_name), gend(_gend), age(_age)
14     {
15         cout << "Person::Person()" << endl;
16     }
17     ~Person()
18     {
19         cout << "Person::~Person()" << endl;
20     }
21 
22     void Print()
23     {
24         cout << "Person::Print 1" << endl;
25     }
26     void Print(const int num)
27     {
28         cout << "Person::Print 2" << endl;
29     }
30     void Print(const string str)
31     {
32         cout << "Person::Print 3" << endl;
33     }
34     void Print(const int num1, const int num2)
35     {
36         cout << "Person::Print 4" << endl;
37     }
38 };

测试函数:

1 void TestAccess()
2 {
3     Person per("", Gender::MALE, 18);
4     per.Print();
5     per.Print(100);
6     per.Print("hello");
7     per.Print(100, 300);
8 }

测试结果:

4.2 继承与重载

(1)子类中没有与父类重载函数同名的成员函数

子类中没有与父类重载函数同名的成员函数时,子类依然可以根据传递的参数类型不同或个数不同,调用到父类不同的函数

Example

父类Person中4个Print()之间构成重载的关系,子类Student的对象调用Print()时,根据传的实参参数类型或个数不同,执行父类不同的Print()。

 1 enum Gender {
 2     MALE,
 3     FEMALE,
 4 };
 5 
 6 class Person
 7 {
 8 private:
 9     string name;
10     Gender gend;
11     int age;
12 public:
13     Person(const string &_name, const Gender _gend, const int _age) : name(_name), gend(_gend), age(_age)
14     {
15         cout << "Person::Person()" << endl;
16     }
17     ~Person()
18     {
19         cout << "Person::~Person()" << endl;
20     }
21 
22     void Print()
23     {
24         cout << "Person::Print 1" << endl;
25     }
26     void Print(const int num)
27     {
28         cout << "Person::Print 2" << endl;
29     }
30     void Print(const string str)
31     {
32         cout << "Person::Print 3" << endl;
33     }
34     void Print(const int num1, const int num2)
35     {
36         cout << "Person::Print 4" << endl;
37     }
38 };
39 
40 class Student : public Person
41 {
42 private:
43     int studentId;
44     int score;
45 public:
46     Student(const int id, const int _score) :  Person("", Gender::MALE, 18), studentId(id), score(_score)
47     {
48         cout << "Student::Student()" << endl;
49     }
50     ~Student()
51     {
52         cout << "Student::~Student()" << endl;
53     }
54 };

测试函数:

1 void TestAccess()
2 {
3     Student stu(10001, 100);
4 
5     stu.Print();
6     stu.Print(100);
7     stu.Print("hello");
8     stu.Print(100, 300);
9 }

测试结果:

(2)子类中存在与父类重载函数同名的成员函数

子类中存在与父类重载函数相同函数名的成员函数时,子类的该函数与父类的重载函数没有关系,此在OOP语言中是C++独有的,称之为名字隐藏(name hidden)

Example

子类中也有Print()函数,此时子类的Print()把父类的Print()隐藏掉了

 1 enum Gender {
 2     MALE,
 3     FEMALE,
 4 };
 5 
 6 class Person
 7 {
 8 private:
 9     string name;
10     Gender gend;
11     int age;
12 public:
13     Person(const string &_name, const Gender _gend, const int _age) : name(_name), gend(_gend), age(_age)
14     {
15         cout << "Person::Person()" << endl;
16     }
17     ~Person()
18     {
19         cout << "Person::~Person()" << endl;
20     }
21 
22     void Print()
23     {
24         cout << "Person::Print 1" << endl;
25     }
26     void Print(const int num)
27     {
28         cout << "Person::Print 2" << endl;
29     }
30     void Print(const string str)
31     {
32         cout << "Person::Print 3" << endl;
33     }
34     void Print(const int num1, const int num2)
35     {
36         cout << "Person::Print 4" << endl;
37     }
38 };
39 
40 class Student : public Person
41 {
42 private:
43     int studentId;
44     int score;
45 public:
46     Student(const int id, const int _score) :  Person("", Gender::MALE, 18), studentId(id), score(_score)
47     {
48         cout << "Student::Student()" << endl;
49     }
50     ~Student()
51     {
52         cout << "Student::~Student()" << endl;
53     }
54 
55     void Print()
56     {
57         cout << "Student::Print()" << endl;
58     }
59 };

测试函数:

1 void TestAccess()
2 {
3     Student stu(10001, 100);
4 
5     stu.Print();
6     // stu.Print(100); /* 编译error */
7     // stu.Print("hello"); /* 编译error */
8     // stu.Print(100, 300); /* 编译error */
9 }

测试结果: