一、继承简介
C++ 中,当定义一个新的类 B 时,如果发现类 B 拥有某个已写好的类 A 的全部特点,此外还有类 A 没有的特点,那么就不必从头重写类 B,而是可以把类 A 作为一个“基类”(也称“父类”),把类 B 写为基类 A 的一个“派生类”(也称“子类”)。这样,就可以说从类 A “派生”出了类 B,也可以说类 B “继承”了类 A。继承是一种封装模型之间关系的抽象,是不同封装模型的层次分类。
- 一个类可以派生自多个类,这意味着,它可以从多个基类继承数据和函数。定义一个派生类,我们使用一个类派生列表来指定基类。类派生列表以一个或多个基类命名,形式如下:
class derived-class: access-specifier base-class
继承关系的特点:
A、子类拥有父类的所有属性和行为
B、子类也是一种特殊的父类
C、子类对象可以当父类对象使用
D、子类中可以添加父类没有的属性和方法
E、子类对象可以直接初始化为父类对象
F、子类对象可以直接赋值给父类对象
G、继承是面向对象编程中代码复用的重要手段继承举例:
#include <iostream> using namespace std; class Parent { public: Parent(int i = 0) { member = i; } void method() { cout << "member = " << member << endl; } private: int member; }; class Child : public Parent { public: Child(int i = 0, int j = 0):Parent(i) { childMember = j; } void childMethod() { method(); cout << "childMember = "<< childMember << endl; } private: int childMember; }; int main(int argc, char *argv[]) { Child child(1,2); child.method(); child.childMethod(); return 0; }
- 派生类的声明:
class 派生类名:[继承方式] 基类名 { 派生类成员声明; };
如果一个派生类同时有多个基类,称为多重继承;如果派生类只有一个基类,称为单继承。
二、继承方式
继承方式规定了如何访问基类继承的成员。继承方式有public、 private,、protected。继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。继承方式如下:
A、公有继承
基类的公有成员和保护成员在派生类中保持原有访问属性,其私有成员仍为基类的私有成员。
B、私有继承
基类的公有成员和保护成员在派生类中成了私有成员,其私有成员仍为基类的私有成员。
C、保护继承
基类的公有成员和保护成员在派生类中成了保护成员,其私有成员仍为基类的私有成员。
继承成员的访问属性 = Max{继承方式,父类成员的访问属性}
三、不同继承方式示例
#include <iostream> using namespace std; class Parent { public: Parent(int a = 0, int b = 0, int c = 0) { pub = a; pro = b; pri = c; } public: int pub; protected: int pro; private: int pri; }; class ChildA : public Parent { public: ChildA(int a = 0, int b = 0):Parent(a, b) { } void print() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; //cout << "pri = " << pri << endl;//error,私有成员不可见 } }; class ChildB : protected Parent { public: ChildB(int a = 0, int b = 0):Parent(a, b) { } void print() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; //cout << "pri = " << pri << endl;//error,私有成员不可见 } }; class ChildC : private Parent { public: ChildC(int a = 0, int b = 0):Parent(a, b) { } void print() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; //cout << "pri = " << pri << endl;//error,私有成员不可见 } }; //默认继承方式 class ChildD : Parent { public: ChildD(int a = 0, int b = 0):Parent(a, b) { } void print() { cout << "pub = " << pub << endl; cout << "pro = " << pro << endl; //cout << "pri = " << pri << endl;//error,私有成员不可见 } }; int main(int argc, char *argv[]) { ChildA childa(1000,2000); childa.pub = 0; //childa.pro = 2000;//error,外部不可见 //childa.pri = 3000;//error,外部不可见 childa.print(); ChildB childb(1001,2001); //childb.pub = 1001;//error,外部不可见 //childb.pro = 2001;//error,外部不可见 //childb.pri = 3001;//error,外部不可见 childb.print(); ChildC childc(1002,2002); //childc.pub = 1002;//error,外部不可见 //childc.pro = 2002;//error,外部不可见 //childc.pri = 3002;//error,外部不可见 childc.print(); ChildD childd(1003,2003); //childd.pub = 1003;//error,外部不可见 //childd.pro = 2003;//error,外部不可见 //childd.pri = 3003;//error,外部不可见 childd.print(); return 0; }
四、派生类的析构函数
派生类的析构函数的功能是在对象销毁前进行一些必要的清理工作,析构函数没有类型,也没有参数。析构函数的调用顺序与构造函数相反。
析构函数只有一种,无重载,无默参。
子类对象销毁时析构函数的调用顺序如下:
A、调用类自身的析构函数
B、调用成员变量的析构函数
C、调用父类的析构函数
五、父类与子类的同名覆盖
子类定义父类中的同名成员时发生同名覆盖,规则如下:
A、子类可以定义父类中的同名成员(成员变量和成员函数)
B、子类中的成员将隐藏父类中的同名成员
C、父类中的同名成员依然存在于子类中
D通过作用域访问符访问父类中的同名成员
如果某派生类的多个基类拥有同名的成员,派生类又新增与基类同名的成员,派生类成员将shadow(隐藏)所有基类的同名成员,需要作用域的调用方式才能调用基类的同名成员。
#include <iostream> using namespace std; class Parent { public: int m_count; void print() { cout << &m_count << endl; } }; class Child : public Parent { public: int m_count; void print() { cout << &(Parent::m_count) << endl; cout << &m_count << endl; } }; int main(int argc, char *argv[]) { Parent p; p.print(); cout << &p.m_count << endl; Child child; child.print(); //子类对象的父类同名成员变量访问 cout << &child.Parent::m_count <<endl; //子类对象的父类同名成员函数访问 child.Parent::print(); cout << &child.m_count << endl; return 0; }
函数重载发生在同一作用域 ,父类和子类的同名函数不构成函数重载,属于同名覆盖,子类会隐藏父类中的同名成员。
子类可以定义父类中的同名成员,子类中的成员将隐藏父类中的同名成员,父类中的同名成员依然存在子类中,通过作用域分辨符::访问父类中的同名成员。
子类中的成员函数将隐藏父类中的同名成员函数,子类无法重载父类中同名成员函数,使用作用域分辨符可以访问父类中的同名成员函数。
#include <iostream> using namespace std; class Parent { public: int mi; void add(int i) { mi += i; } void add(int a, int b) { mi += (a + b); } }; class Child : public Parent { public: int mi; void add(int x, int y, int z) { mi += (x + y + z); } }; int main(int argc, char *argv[]) { Parent p; p.add(1); p.add(1,2); Child child; child.add(1,2,3); //child.add(1);//error child.Parent::add(1); //child.add(1,2);//error child.Parent::add(1,2); return 0; }