# 三种继承方式 继承的一般语法为: ```c++ class 派生类名: [继承方式] 基类名{ 派生类新增的成员 }; ``` 继承方式限定了基类成员在派生类中的访问权限,包括 public(公有的)、private(私有的)和 protected(受保护的)。此项是可选项,如果不写,默认为 private(成员变量和成员函数默认也是 private)。 现在我们知道,public、protected、private 三个关键字除了可以修饰类的成员,还可以指定继承方式。 ## public、protected、private 修饰类的成员 类成员的访问权限由高到低依次为 public --> protected --> private,具体的 public 和 private :public 成员可以通过对象来访问,private 成员不能通过对象访问。 现在再来补充一下 protected。protected 成员和 private 成员类似,也不能通过对象访问。但是当存在继承关系时,protected 和 private 就不一样了:基类中的 protected 成员可以在派生类中使用,而基类中的 private 成员不能在派生类中使用。 ## public、protected、private 指定继承方式 不同的继承方式会影响基类成员在派生类中的访问权限。 **1) public继承方式** - 基类中所有 public 成员在派生类中为 public 属性; - 基类中所有 protected 成员在派生类中为 protected 属性; - 基类中所有 private 成员在派生类中不能使用。 **2) protected继承方式** - 基类中的所有 public 成员在派生类中为 protected 属性; - 基类中的所有 protected 成员在派生类中为 protected 属性; - 基类中的所有 private 成员在派生类中不能使用。 **3) private继承方式** - 基类中的所有 public 成员在派生类中均为 private 属性; - 基类中的所有 protected 成员在派生类中均为 private 属性; - 基类中的所有 private 成员在派生类中不能使用。 通过上面的分析可以发现: 1) 基类成员在派生类中的访问权限不得高于继承方式中指定的权限。例如,当继承方式为 protected 时,那么基类成员在派生类中的访问权限最高也为 protected,高于 protected 的会降级为 protected,但低于 protected 不会升级。再如,当继承方式为 public 时,那么基类成员在派生类中的访问权限将保持不变。也就是说,继承方式中的 public、protected、private 是用来指明基类成员在派生类中的最高访问权限的。 2) 不管继承方式如何,基类中的 private 成员在派生类中始终不能使用(不能在派生类的成员函数中访问或调用)。 3) 如果希望基类的成员能够被派生类继承并且毫无障碍地使用,那么这些成员只能声明为 public 或 protected;只有那些不希望在派生类中使用的成员才声明为 private。 4) 如果希望基类的成员既不向外暴露(不能通过对象访问),还能在派生类中使用,那么只能声明为 protected。 注意,我们这里说的是基类的 private 成员不能在派生类中使用,并没有说基类的 private 成员不能被继承。实际上,基类的 private 成员是能够被继承的,并且(成员变量)会占用派生类对象的内存,它只是在派生类中不可见,导致无法使用罢了。private 成员的这种特性,能够很好的对派生类隐藏基类的实现,以体现面向对象的封装性。 | 继承方式/基类成员 | public成员 | protected成员 | private成员 | | ----------------- | ---------- | ------------- | ----------- | | public继承 | public | protected | 不可见 | | protected继承 | protected | protected | 不可见 | | private继承 | private | private | 不可见 | 由于 private 和 protected 继承方式会改变基类成员在派生类中的访问权限,导致继承关系复杂,所以实际开发中我们一般使用 public。 【示例】演示类的继承关系。 ```c++ #include using namespace std; //基类 People class People { public: void setname(char *name); void setage(int age); void sethobby(char *hobby); char *gethobby(); protected: char *m_name; int m_age; private: char *m_hobby; }; void People::setname(char *name) { m_name = name; } void People::setage(int age) { m_age = age; } void People::sethobby(char *hobby) { m_hobby = hobby; } char *People::gethobby() { return m_hobby; } //派生类Student class Student : public People { public: void setscore(float score); protected: float m_score; }; void Student::setscore(float score) { m_score = score; } //派生类 Pupil class Pupil : public Student { public: void setranking(int ranking); void display(); private: int m_ranking; }; void Pupil::setranking(int ranking) { m_ranking = ranking; } void Pupil::display() { cout << m_name << "的年龄是" << m_age << ", 考试成绩为: " << m_score << "分,班级排名: " << m_ranking << ", TA的爱好是" << gethobby() << "。"; } int main() { char name[] = "汪跃东"; char hobby[] = "足球"; Pupil pup; pup.setname(name); pup.setage(18); pup.setscore(88.5f); pup.setranking(4); pup.sethobby(hobby); pup.display(); return 0; } ``` 这是一个多级继承的例子,Student 继承自 People,Pupil 又继承自 Student,它们的继承关系为 People --> Student --> Pupil。Pupil 是最终的派生类,它拥有基类的 m_name、m_age、m_score、m_hobby 成员变量以及 setname()、setage()、sethobby()、gethobby()、setscore() 成员函数。 注意,在派生类 Pupil 的成员函数 display() 中,我们借助基类的 public 成员函数 gethobby() 来访问基类的 private 成员变量 m_hobby,因为 m_hobby 是 private 属性的,在派生类中不可见,所以只能借助基类的 public 成员函数 sethobby()、gethobby() 来访问。 在派生类中访问基类 private 成员的唯一方法就是借助基类的非 private 成员函数,如果基类没有非 private 成员函数,那么该成员在派生类中将无法访问。 ## 改变访问权限 使用 using 关键字可以改变基类成员在派生类中的访问权限,例如将 public 改为 private、将 protected 改为 public。 注意:using 只能改变基类中 public 和 protected 成员的访问权限,不能改变 private 成员的访问权限,因为基类中 private 成员在派生类中是不可见的,根本不能使用,所以基类中的 private 成员在派生类中无论如何都不能访问。 ```c++ #include using namespace std; //基类People class People { public: void show(); protected: char *m_name; int m_age; }; void People::show() { cout << m_name << "的年龄是" << m_age << endl; } //派生类Student class Student : public People { public: void learning(); public: using People::m_name; //将protected改为public using People::m_age; //将protected改为public float m_score; private: using People::show; //将public改为private }; void Student::learning() { cout << "我是" << m_name << ",今年" << m_age << "岁,这次考了" << m_score << "分!" << endl; } int main() { Student stu; stu.m_name = "小明"; stu.m_age = 16; stu.m_score = 99.5f; stu.show(); //compile error stu.learning(); return 0; } ``` 代码中首先定义了基类 People,它包含两个 protected 属性的成员变量和一个 public 属性的成员函数。定义 Student 类时采用 public 继承方式,People 类中的成员在 Student 类中的访问权限默认是不变的。 不过,我们使用 using 改变了它们的默认访问权限,如代码第 21~25 行所示,将 show() 函数修改为 private 属性的,是降低访问权限,将 name、age 变量修改为 public 属性的,是提高访问权限。 ## 继承时的名字遮蔽问题 如果派生类中的成员(包括成员变量和成员函数)和基类中的成员重名,那么就会遮蔽从基类继承过来的成员。所谓遮蔽,就是在派生类中使用该成员(包括在定义派生类时使用,也包括通过派生类对象访问该成员)时,实际上使用的是派生类新增的成员,而不是从基类继承来的。 下面是一个成员函数的名字遮蔽的例子: ```c++ #include using namespace std; //基类 People class People { public: void show(); protected: char *m_name; int m_age; }; void People::show() { cout << "嗨,大家好,我叫" << m_name << ",今年" << m_age << "岁。" << endl; } //派生类 Student class Student : public People { public: Student(char *name, int age, float score); public: void show(); //遮蔽基类中的show()函数 private: float m_score; }; Student::Student(char *name, int age, float score) { m_name = name; m_age = age; m_score = score; } void Student::show() { cout << m_name << "的年龄是" << m_age << ",成绩是" << m_score << endl; } int main() { char name[] = "汪跃东"; Student stu(name, 18, 49.5f); //使用的是派生类新增的成员函数,而不是从基类继承的 stu.show(); //使用的是从基类继承来的成员函数 //stu.People::show(); return 0; } ``` 本例中,基类 People 和派生类 Student 都定义了成员函数 show(),它们的名字一样,会造成遮蔽。第 37 行代码中,stu 是 Student 类的对象,默认使用 Student 类的 show() 函数。但是,基类 People 中的 show() 函数仍然可以访问,不过要加上类名和域解析符,如第 39 行代码所示。 ## 基类成员函数和派生类成员函数不构成重载 基类成员和派生类成员的名字一样时会造成遮蔽,这句话对于成员变量很好理解,对于成员函数要引起注意,不管函数的参数如何,只要名字一样就会造成遮蔽。换句话说,基类成员函数和派生类成员函数不会构成重载,如果派生类有同名函数,那么就会遮蔽基类中的所有同名函数,不管它们的参数是否一样。 ```c++ #include using namespace std; //基类Base class Base{ public: void func(); void func(int); }; void Base::func(){ cout<<"Base::func()"< using namespace std; class A{ public: void func(); public: int n; }; void A::func(){ cout<<"c.biancheng.net"< using namespace std; //基类Base class Base{ public: void func(); void func(int); }; void Base::func(){ cout<<"Base::func()"<