继承是使代码可以复用的重要手段,也是面向对象程序设计的核心思想之一。简单的说,继承是指一个对象直接使用另一对象的属性和方法。
C++ 中的继承关系就好比现实生活中的父子关系,继承一笔财产比白手起家要容易得多,原始类称为基类,继承类称为派生类,基类是对派生类的抽象,派生类是对基类的具体化。它们是类似于父亲和儿子的关系,所以也分别叫父类和子类。而子类又可以当成父类,被另外的类继承。
派生类的声明方式声明派生类的一般格式为:
class 派生类名:继承方式 基类名{
派生类新增加的成员
};
继承方式包括:public,protected,private,继承方式是可选的,如果不写继承方式,默认的是private
样例1
给出文件
#ifndef PEO_H_ #define PEO_H_ #include#include using namespace std; class People{ public: string Name; void PrintName(); }; void People::PrintName(){ cout << "姓名:" << Name << endl; } #endif
#include "usr.h"
int main(){
int id;
string name;
cin >> id >> name ;
Student st;
Set(id,name,&st);
st.PrintSID();
st.PrintName();
}
要求实现文件
#include "people.h" #include保护继承#include using namespace std; class Student:public People{ private: int SID; public: void PrintSID(); friend void Set(int sid,string name,Student *ptr); }; void Student::PrintSID(){ cout << "学号:" << SID << endl; } void Set(int sid,string name,Student *ptr){ ptr->SID=sid; ptr->Name=name; }
保护继承相对于公有继承,访问性有所降低,父类的公有成员在子类中变成了保护成员,也就无法在外部通过一个对象访问父类成员了,但是对于这个子类的子类仍然是可见的(因为可见性只是降到了 protected )。
如果要保护继承一个类,只需继承时在类名前面加上 protected 关键字即可。
样例1
给出文件
#ifndef PEO_H_ #define PEO_H_ #include#include using namespace std; class People{ public: string Name; void PrintName(); }; void People::PrintName(){ cout << "姓名:" << Name << endl; } #endif
#include "usr.h"
int main(){
int id;
string name;
cin >> id >> name ;
Student st;
Set(id,name,&st);
st.PrintSID();
((People*)&st)->PrintName();
}
要求实现文件
注意到main()中Set()为正常函数(不是成员函数),并且需要访问protected类型的People,所以这里声明为类外的友元函数
#include "people.h" #include重载、重定义(隐藏、覆盖)、重写#include using namespace std; class Student:protected People{ public: int SID; void PrintSID(); friend void Set(int sid,string name,Student* ptr); }; void Student::PrintSID(){ cout << "学号:" << SID << endl; } void Set(int sid,string name,Student* ptr){ ptr->SID=sid; ptr->Name=name; }
重载
产生原因:主要是因为在C++中,编译器在编译.cpp文件中当前作用域的同名函数时,函数生成的符号由返回值类型(不起决定作用)+形参类型和顺序(起决定作用)的组成。
作用:用同一个函数名命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。
重定义
重定义也叫隐藏,指的是在继承关系中,子类实现了一个和父类名字一样的函数,(只关注函数名,和参数与返回值无关)这样的话子类的函数就把父类的同名函数隐藏了。
下面给出一个易错的例子:
class A{
public:
int _x;
void f(){ cout << "A" << endl; }
};
class B:public A{
public:
void f(int a){ cout << "B" << endl; }
};
void test(){
B b;
b.f();
}
上述例子会报错,因为B中的f()覆盖了父类A的f(),所以函数定义是f(int a);
重写
重写指的在继承关系中,子类中定义了一个与父类极其相似的虚函数。
具体怎么相似:函数名必须相同,参数列表必须相同,返回值可以不相同,但是必须是父子关系的指针或引用。通过重写,可以实现动态多态,何为动态多态,就是当父类的指针或引用指向被重写的虚函数时,父类的指针或引用指向谁就调用谁的虚函数,而不是说根据类型。
在这里,如果去掉父类的虚函数的virtual,则构不成多态,如果去掉子类虚函数的virtual可以构成多态,可以理解为编译器优化。
样例
给出文件
#ifndef PEO_H_ #define PEO_H_ #include#include using namespace std; class People{ public: string Name; void PrintName(); }; void People::PrintName(){ cout << "姓名:" << Name << endl; } #endif
#include "usr.h"
int main(){
int i,j;
string name;
cin >> i >> j >> name;
Graduate st;
Set(name,i,j,&st);
((Student*)&st)->PrintSID();
((People*)&st)->PrintName();
st.PrintResearchID();
}
要求实现文件
注意到Student与Graduate中均有Set(),当方法名相同时,子类的函数会覆盖父类的函数(尽管他们的传参列表不相同)(但是可以通过作用域限制来访问Student::Set())(覆盖),
class Student:private People{
public:
int SID;
void PrintSID(){ cout<<"学号:"<Set(string name,int sid);//调用的是Graduate中的成员函数
ptr->ResearchID=rid;
}



