赞
踩
在
C
+
+
{\rm C++}
C++中,继承和派生其实两个相对的概念。它们的定义如下:我们在定义一个新的类B
时,如果该类与某个已有的类A
相似(指B
拥有A
的全部特点),那么就可以把A
作为基类,B
称为派生类。即类B
继承自类A
,或类A
派生出类B
,这就是二者包含相对性的概念。基类和派生类的性质有:
现实生活中,一个典型的需要使用继承/派生的例子就是学生类和研究生类。如上图,学生拥有姓名、性别、学号等属性,拥有入学、毕业等方法;而研究生在学生的基础上可能还有导师、系别等额外属性,拥有当助教等额外方法。因此,研究生包含了学生的全部特点,我们在定义研究生类的时候可以将学生类作为基类,研究生作为派生类。在
C
+
+
{\rm C++}
C++中,派生的定义形式如下(通常使用公有继承):
class 派生类名: public 基类名
{
...
};
下面以一个具体的例子来说明基类和派生类的相关概念:
// 学生类 class CStudent { private: string sName; // 姓名 int nAge; // 年龄 public: void enrol() {}; // 入学 bool IsThreeGood() {}; // 三好学生 void SetName(const string& name) { sName = name; } }; // 本科生类,继承自学生类 class CUndergraduateStuden :public CStudent { private: int nDepartment; // 系别 public: void enrol() {}; // 覆盖基类方法 void PostgraduateRecommendation() {}; // 保研 }; // 研究生类,继承自学生类 class CGraduateStudent :public CStudent { private: int nDecpartment; // 系别 char szMentorName[20]; // 导师 public: void enrol() {}; // 覆盖基类的方法 void DoTeachingAssistant() {}; // 当助教 };
下面根据继承的相关内容编写一个简单的学籍管理程序,具体内容可参考代码注释:
// 学生类 class CStudent { private: string name; // 姓名 string id; // 学号 char gender; // 性别,F和M分别代表女和男 int age; // 年龄 public: void PrintInfo(); // 打印学生信息 void SetInfo(const string& _name, const string& _id, int _age, char _gender); // 为对象赋值 // 返回姓名 string GetName() { return name; } }; // 打印学生信息 void CStudent::PrintInfo() { cout << "Name:" << name << endl; cout << "Id:" << id << endl; cout << "Gender:" << gender << endl; cout << "Age:" << age << endl; } // 为对象赋值 void CStudent::SetInfo(const string& _name, const string& _id, int _age, char _gender) { name = _name; id = _id; age = _age; gender = _gender; } // 本科生类,继承自学生类 class CUndergraduateStudent :public CStudent { private: string department; // 系别 public: // 保研 void PostgraduateRecommendation() { cout << "Qualified for Postgraduate Recommendation!" << endl; }; // 派生类的函数覆盖基类的函数 void PrintInfo() { CStudent::PrintInfo(); // 调用基类的方法输出共有信息 cout << "Department:" << department << endl; } // 派生类的函数覆盖基类的函数 void SetInfo(const string& _name, const string& _id, int _age, char _gender, const string& _department) { CStudent::SetInfo(_name, _id, _age, _gender); // 调用基类的方法设置共有信息 department = _department; } }; int main() { // 创建本科生对象CUS CUndergraduateStudent CUS; // 设置姓名、学号、年龄、性别、系别 CUS.SetInfo("XiaoMin", "20200613", 20, 'M', "Computer Science"); // 返回姓名 cout << CUS.GetName() << " "; // 设置保研资格 CUS.PostgraduateRecommendation(); // 打印全部信息 CUS.PrintInfo(); return 0; }
上述主函数的输出结果如下图:
在
C
+
+
{\rm C++}
C++中,覆盖的定义是:派生类可以定义一个和基类成员同名的成员(成员变量或成员函数),这就是覆盖的概念。在派生类中访问这类成员时,默认情况就是访问派生中定义的成员。如果要在派生类中访问由基类定义的同名成员时,需加上作用域运算符::
。上面例子中的本科生类的PrintInfo
和SetInfo
方法均是对基类方法的覆盖,在派生类内实现该方法时使用CStudent::
调用基类的方法。注意,一般情况下,我们在基类中定义成员变量时变量名与基类的变量名不一致,成员函数的函数名则通常可以定义为相同。
前面我们在定义成员时,通常将其定义为共有或私有,对应关键字public
和private
。在
C
+
+
{\rm C++}
C++中存在另外一种存取访问权限说明符:protected
,即定义保护成员。我们首先来看三种存取访问权限说明符的作用情况(以基类的成员为例说明):
访问权限说明符 | 访问权限 |
---|---|
private | 基类的成员函数 |
基类的友元函数 | |
public | 基类的成员函数 |
基类的友元函数 | |
派生类的成员函数 | |
派生类的友元函数 | |
其他函数 | |
protected | 基类的成员函数 |
基类的友元函数 | |
派生类的成员函数可以访问当前对象的基类的成员函数 |
由上表我们可以看到,protected
的访问权限介于public
和private
之间。在public
的基础上,protected
可以在派生类中访问相应基类的成员。现以下面例子说明:
class A { private: int nPrivate; // 私有成员 protected: int nProtected; // 保护成员 public: int nPublic; // 公有成员 }; class B :public A { void fun() { nPrivate = 1; // ERROR,派生类无法访问基类的私有成员 nProtected = 1; // OK,派生类可以访问基类的保护成员,即函数fun所作用的对象可以访问该函数 nPublic = 1; // OK,派生类可以访问基类的公有成员 } }; int main() { A a; B b; a.nPublic = 1; // OK b.nPublic = 1; // OK,访问公有成员 a.nProtected = 1; // ERROR a.nPrivate = 1; // ERROR,类外不能访问保护成员和私有成员 b.nProtected = 1; // ERROR b.nPrivate = 1; // ERROR,派生类外派生类成员不能访问保护成员和私有成员 return 0; }
一般情况下,我们不会使用protected
,public
和private
就足以实现成员的存取访问功能。
有时候我们需要同时在基类和派生类中定义构造函数,但在定义派生类的构造函数时我们需要注意对基类成员的访问权限。以下面的例子说明:
// 昆虫类 class Insect { private: int nLegs; // 腿条数 int nColor; // 颜色数 public: int nType; // 类型数 Insect(int legs, int color, int type); // 构造函数 void PrintInsect() {}; // 打印昆虫信息 }; // 基类构造函数的实现 Insect::Insect(int legs, int color, int type) { nLegs = legs; nColor = color; nType = type; } // 飞虫类继承自昆虫类 class FlyInsect :public Insect { int nWings; // 翅膀数 public: FlyInsect(int legs, int color, int wings); // 构造函数 }; // 派生类构造函数的实现 FlyInsect::FlyInsect(int legs, int color, int wings) { nLegs = legs; // ERROR nColor = color; // ERROR,不能访问基类的私有成员 nType = 1; // OK nWings = wings; // OK }
由上面程序可以看到,如果我们以基类实现构造函数的方式来实现派生类的构造函数,由于基类的私有成员不可访问,所以程序会出现访问权限的错误。这里的解决办法是在实现派生类构造函数时使用初始化列表,如下:
FlyInsect::FlyInsect(int legs, int color, int wings): Insect(legs, color) {
nType = 1;
nWings = wings;
}
或写作:
FlyInsect::FlyInsect(int legs, int color, int wings) : Insect(legs, color, 1), nWings(wings) {};
总之,在创建派生类的对象时,需要调用基类的构造函数:初始化派生类的对象中从基类继承的成员(主要解决在派生类中不能访问基类中的某些成员的问题)。在执行一个派生类的构造函数前,总是先执行基类的构造函数;与此对应的是派生类的析构函数被执行时,总是先执行派生类的构造函数,再执行基类的构造函数。 最后,在派生类中调用基类构造函数的形式:
假如有如下类定义:
class base {}; // 基类
class derived: public base {}; // 公有继承的派生类
base b; // 基类对象
derived d; // 派生类对象
公有继承的赋值兼容规则如下:
b=d;
。在赋值号没有重载的情况下,d=b;
会报错;base& br=d;
;base* pb=&d;
;注意,如果派生类不是公有继承(私有继承或保护继承),上述三条规则不成立。总而言之,派生类对象是一个基类对象,而基类对象不是派生类对象。
如果有如下关系:类A
派生出类B
,类B
派生出类C
,类C
派生出类D
,则:
A
是类B
的直接基类;B
是类C
的直接基类,类A
是类C
的间接基类;C
是类D
的直接基类,类A
和类B
是类D
的间接基类。在声明派生类时,只需要列出它的直接基类,如class D: public C {};
。派生类会沿着类的层次自动向上继承自它的间接基类;这时,派生类的成员包括自己的成员、直接基类的成员和所有间接基类的全部成员。下面是一个多重继承的例子:
// 基类 class A { public: int n; A(int i) :n(i) { cout << "A" << n << "Constructed!" << endl; } ~A() { cout << "A" << n << "Destructed!" << endl; } }; // 中间类继承自类A class B :public A { public: // 调用基类的构造函数初始化 B(int i) :A(i) { cout << "B" << n << "Constructed!" << endl; } ~B() { cout << "B" << n << "Destructed!" << endl; } }; // 派生类继承自类B,这里不用标识间接基类A class C :public B { public: C() :B(1) { cout << "C" << n << "Constructed!" << endl; } ~C() { cout << "C" << n << "Destructed!" << endl; } }; int main() { C c; return 0; }
上面程序的输出结果是:
由上面的介绍,我们可以看到, C + + {\rm C++} C++引入继承和派生的概念,一方面可以减少相似或重复的代码量,另一方面可以在类与类之间建立起紧密联系。在实际编程中,继承的概念非常普遍,往往通过类与类之间的继承和派生就可以建立起一套庞大的、条理清晰的、泛化性好的面向对象体系。在《 C + + {\rm C++} C++学习》这一系列的博文中,我们一步步剖析面向对象编程的实质,从抽象将客观存在的事物抽象为计算机语言、封装将某种具有相同或相似属性的群体封装成一个类、到现在利用继承和派生建立起类与类之间紧密的联系。后文我们将继续介绍面向对象编程里最后一种基本特征,多态。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。