在C++中,所谓“继承”就是在一个已存在的类的基础上建立一个新的类。已存在的类成为“基类”,新建立的类成为“派生类”
一个新类从已有的类那里获得其已有特性,这种现象成为累的继承。从另一角度说,从已有的类产生一个新的子类,称谓累的派生。
派生类的声明方式:
class 派生类名:[继承方式] 基类名
{
派生类新增加的成员;
} ;
继承方式包括:public(公用的),private(私有的)和protected(受保护的),继承方式是可选的,如果不写此项,则默认为private(私有的)。
一个简单的公用继承的例子:
#include <iostream>
using namespace std;
class student
{
public:
student()
{
num=1001;
name="shan";
sec='m';
}
void get_v()
{
cin>>num>>name>>sec;
}
void display()
{
cout<<"num= "<<num<<endl;
cout<<"name= "<<name<<endl;
cout<<"sec= "<<sec<<endl;
}
private:
int num;
string name;
char sec;
};
class student1:public student
{
public:
student1 ()
{
age=22;
addr="jisuanji";
}
void display1()
{
cout<<"age= "<<age<<endl;
cout<<"addr= "<<addr<<endl;
}
private:
int age;
string addr;
};
int main()
{
student1 s1;
s1.display();
s1.display1();
return 0;
}
由于私有派生类限制太多,使用不方便,一般不常使用。
而保户继承的特点是:保护基类的公用成员和保户成员在派生类中都成了保户成员,气死有成员仍为基类私有。
在派生类中引用保户成员。
#include <iostream>
using namespace std;
class student
{
public:
student()
{
num=1001;
name="shan";
sec='m';
}
void get_v()
{
cin>>num>>name>>sec;
}
void display()
{
cout<<"num= "<<num<<endl;
cout<<"name= "<<name<<endl;
cout<<"sec= "<<sec<<endl;
}
protected:
int num;
string name;
char sec;
};
class student1:public student
{
public:
student1 ()
{
age=22;
addr="jisuanji";
}
void display1()
{
cout<<"num= "<<num<<endl;
cout<<"name= "<<name<<endl;
cout<<"sec= "<<sec<<endl;
cout<<"age= "<<age<<endl;
cout<<"addr= "<<addr<<endl;
}
private:
int age;
string addr;
};
int main()
{
student1 s1;
s1.display();
s1.display1();
return 0;
}
在派生类的成员函数中引用积累的保户成员是合法的。基类的保护成员对派生类的外界来说是不可访问的。保护成员和私用成员不同之处,在于把保护成员的访问范围扩展到派生类中。
无论哪一种继承方式,在派生类中是不能访问基类的私有成员的,私有成员只能被本类的成员函数所访问,毕竟派生类雨基类不是同一个类。如果在多级派生时都采用公用继承方式,那么直到最后一级派生类都能访问基类的公用成员和保护成员。如果采用私用继承方式,经过若干次派生之后,基类的所有成员已经变成不可访问的了。如果采用保护继承方式,在派生类外是无法访问派生类中的任何成员的。
派生类的构造函数和析构函数
简单派生类的构造函数:
#include <iostream>
using namespace std;
class student
{
public:
student(int n,string nam,char s)
{
num=n;
name=nam;
sec=s;
}
protected:
int num;
string name;
char sec;
};
class student1:public student
{
public:
student1 (int n,string nam,char s,int a,string ad):student(n,nam,s)
{
age=a;
addr=ad;
}
void display1()
{
cout<<"num= "<<num<<endl;
cout<<"name= "<<name<<endl;
cout<<"sec= "<<sec<<endl;
cout<<"age= "<<age<<endl;
cout<<"addr= "<<addr<<endl;
}
private:
int age;
string addr;
};
int main()
{
student1 s1(10010,"wang",'f',19,"jinan");
student1 s2(10020,"zhang",'f',20,"zibo");
s1.display1();
s2.display1();
return 0;
}
注意派生类构造函数的首行的写法:
Student1(int n,string nam,char s,int a,string ad):student(n,nam,s)
其一般形式为:
派生类构造函数名(总参数列表):基类构造函数名(参数列表)
{派生类中新增数据成员初始化语句}
如果想在类外定义派生类的构造函数,可如下:只在类体中写函数声明:
Student1(int n,string nam,char s,int a,string ad);
在类的外面定义派生类的构造函数:
Student1 :: student1(int n,string nam,char s,int a,string ad)
{
Age=a;
Addr=ad;
}
有子对象的派生类的构造函数:
#include <iostream>
#include <string>
using namespace std;
class student
{
public:
student(int n,string nam)
{
num=n;
name=nam;
}
void display()
{
cout<<"num= "<<num<<endl<<"name= "<<name<<endl;
}
protected:
int num;
string name;
};
class student1:public student
{
public:
student1 (int n,string nam,int n1,string nam1,int a,string ad):student(n,nam),monitor(n1,nam1)
{
age=a;
addr=ad;
}
void display1()
{
cout<<"this student is: "<<endl;
display();
cout<<"age= "<<age<<endl;
cout<<"addr= "<<addr<<endl;
}
void show_monitor()
{
cout<<endl<<"class monitor is: "<<endl;
monitor.display();
}
private:
student monitor;//定义子对象
int age;
string addr;
};
int main()
{
student1 s1(10010,"wang",10001,"shan",19,"jinan");
s1.display1();
s1.show_monitor(); //输出子对象的数据
return 0;
}
可以看到,“班长”的类型不是简单类型(如int,char float等),他是student类的对象。子对象的初始化是在建立派生类时通过调用派生类的构造函数来实现的。
派生类的构造函数任务应该包括三个部分:
(1)对积累数据成员初始化
(2)对子对象数据成员初始化
(3)对派生类数据成员初始化
程序中派生类构造函数首部如下:
Student (int n,string nam,int n1,string nam1,int a,string ad):
Student (n,nam),monitor(n1,nam1);
在上面的构造函数中有六个形参,前两个作为基类构造函数的参数,第三四个作为子对象构造函数的参数,第五六个是用作派生类数据成员初始化的。
归纳起来,定义派生类构造函数的一般形式为:
派生类构造函数名(总参数列表):基类构造函数函数名(参数列表),子对象名(参数列表)
{派生类中新增加数据成员初始化语句}
执行派生类构造函数的顺序是:
(1)调用基类构造函数,对基类数据成员初始化
(2)调用子对象构造函数,对子对象数据成员初始化
(3)再执行派生类构造函数本身,对派生类数据成员初始化
编译系统是根据相同的参数名(而不是根据参数的顺序)来确定他们的传递关系。
多层派生时的构造函数:
#include <iostream>
#include <string>
using namespace std;
class student
{
public:
student(int n,string nam)
{
num=n;name=nam;
}
void display()
{
cout<<"num= "<<num<<endl<<"name= "<<name<<endl;
}
protected:
int num;
string name;
};
class student1:public student
{
public:
student1(int n,string nam,int a):student(n,nam)
{
age=a;
}
void show()
{
display();
cout<<"age= "<<age<<endl;
}
private:
int age;
};
class student2:public student1
{
public:
student2(int n,string nam,int a,int s):student1(n,nam,a)
{
score=s;
}
void show_all()
{
show();
cout<<"score= "<<score<<endl;
}
private:a
int score;
};
int main()
{
student2 s2(10010,"li",17,89);
s2.show_all();
return 0;
}
不要列出每一层派生类的构造函数,只需写出起上一层派生类(即他的直接基类)的构造函数即可。
处暑化的顺序是:
(1)先初始化基类的数据成员num和name。
(2)在初始化student1的数据成员age。
(3)最后再初始化student2的数据成员score。
多重集成派生类的构造函数
声明一个教师(teacher)类和一个学生类(student),用多重继承的方式声明一个研究生(graduate)派生类。教师类中包括数据成员name(姓名),age(年龄),title(职称)。学生类中包括数据成员name1(姓名),sex(性别),score(成绩)。在定义派生类对象时给出初始化的数据,然后输出这些数据。
#include <iostream>
#include <string>
using namespace std;
class teacher
{
public:
teacher(string nam,int a,string t)
{
name=nam;
age=a;
title=t;
}
void display()
{
cout<<"name= "<<name<<endl<<"age= "<<age<<endl<<"title= "<<title<<endl;
}
protected:
string name;
int age;
string title;
};
class student
{
public:
student(string nam1,char x,float s)
{
name1=nam1;
sex=x;
score=s;
}
void display1()
{
cout<<"name= "<<name1<<"sex= "<<sex<<"score= "<<score<<endl;
}
protected:
string name1;
char sex;
float score;
};
class graduate:public teacher,public student
{
public:
graduate(string nam,int a,string t,float s,char x,float w):teacher(nam,a,t),student(nam,x,s)
{
wage=w;
}
void show()
{
cout<<"name= "<<name<<endl;
cout<<"age= "<<age<<endl;
cout<<"sex= "<<sex<<endl;
cout<<"score= "<<score<<endl;
cout<<"title= "<<title<<endl;
cout<<"wage= "<<wage<<endl;
}
private:
float wage;
};
int main()
{
graduate s2("wang",24,"monitor",89.5,'f',1234.5);
s2.show();
return 0;
}
由于在两个基类中把数据成员声明为protected,因此可以通过派生类的成员函数引用积累的成员。如果在基类中把数据成员声明为private则派生类成员函数不能引用这些数据。
这里有一个小小的问题,在两个基类中分别用name和name1来代表姓名,其实这是同一个人的名字,从graduate类的构造函数中可以看到从参数列表中的参数nam分别传递给两个基类的构造函数,作为基类构造函数的实参。现在两个基类都需要有姓名这一项,那么能否用同一个姓名name来代表呢?实际上,在本程序中制作这样的修改是不行的,因为在同一个怕死恒类中存在这两个同名耳朵数据成员,在派生类的成员函数show中引用name时就会出现二义性,编译系统无法判断选择哪一个基类中的name。
多重继承引起的二义性问题
例如:class A
{
public:
int a;
void display();
};
class B
{
public:
int a;
void display();
};
class C:public A,public B
{
public:
int b;
void show();
};
int main()
{
C c1;
c1.a=3;
c1.display();
}
由于基类A和基类B都有数据成员a和成员函数display,编译系统无法判断要访问的是哪一个基类的成员,因此编译程序出错。这个问题可以用基类名来限定,如:
c1.A::a=3;
c1.A::display();
如果是在派生类C中通过派生类成员函数show访问基类A的display和a,可以直接写成
A::a=3;
A::display();
如果两个基类和派生类三者都有同名成员。将上面的C类声明改为 :
class C:public A,public B
{
int a;
void display();
};
此时编译能通过,也能正常运行。那么执行时访问的是哪一个类中的成员?答案是:访问的是派生类C的成员。规则是:基类的同名成员在派生类中被屏蔽,成为“不可见”的,派生类新增加的同名成员覆盖了基类中的同名成员。
虚基类
如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员。
声明虚基类的一般形式为:
Class 派生类名:virtual 继承方式 基类名
虚基类的简单应用举例:
#include <iostream>
#include <string>
using namespace std;
class person
{
public:
person(string nam,char s,int a)
{
name=nam;
sex=s;
age=a;
}
protected:
string name;
char sex;
int age;
};
class teacher:virtual public person
{
public:
teacher(string nam,char s,int a,string t):person(nam,s,a)
{
title=t;
}
protected:
string title;
};
class student:virtual public person
{
public:
student(string nam,char s,int a,float sco):person(nam,s,a)
{
score=sco;
}
protected:
float score;
};
class graduate:public teacher,public student
{
public:
graduate(string nam,char s,int a,string t,float sco,float w):
person(nam,s,a),teacher(nam,s,a,t),student(nam,s,a,sco)
{
wage=w;
}
void show()
{
cout<<"name= "<<name<<endl;
cout<<"age= "<<age<<endl;
cout<<"sex= "<<sex<<endl;
cout<<"score= "<<score<<endl;
cout<<"title= "<<title<<endl;
cout<<"wages= "<<wage<<endl;
}
private:
float wage;
};
int main()
{
graduate g1("wang",'f',24,"student",89.5,1234.5);
g1.show();
return 0;
}
说明:
(1)person类是表示一般人员属性的公用类,其中包括人员的基本数据,现代只包括三个数据成员:name(姓名),sex(性别),age(年龄)。teacher和student类是person的公用类派生类,在teacher类中增加title(职称),在student类中增加score(成绩)。Graduate(研究生)是teacher类和student累的派生类,在graduate类中增加了wage(工资)。一个研究生应该包含以上全部数据。
(2)在graduate类中,只保留一份基类的成员,因此可以用graduate类中的show函数引用graduate类对象的公共积累person的数据成员name,sex,age的值,不需要加基类名和域运算符(::),不会产生二义性。
基类与派生类的转换
三种继承方式中,只有共有继承能较好耳朵保留基类的特征,他保留了除构造函数和析构函数以外的基类所有成员,积累的公用活保护成员的访问权限在派生类中全部都按原样保留下来了,在派生类外可以调用基类的公用成员函数访问积累的私有成员。因此,公用派生类具有基类的全部功能,所有基类能够实现的功能,公用派生类都能实现。
定义一个基类student(学生),再定义student类的公用派生类graduate(研究生),用指向积累对象的指针输出数据。
#include <iostream>
#include <string>
using namespace std;
class student
{
public:
student(int ,string ,float);
void display();
private:
int num;
string name;
float score;
};
student::student(int n,string nam,float sco)
{
num=n;
name=nam;
score=sco;
}
void student::display()
{
cout<<"num= "<<num<<endl;
cout<<"name= "<<name<<endl;
cout<<"score= "<<score<<endl;
}
class graduate:public student
{
public:
graduate(int n,string nam,float sco,float w):
student(n,nam,sco)
{
wage=w;
}
void display();
private:
float wage;
};
void graduate::display()
{
student::display();
cout<<"wage= "<<wage<<endl;
}
int main()
{
student s1(1001,"li",88);
graduate g1(2001,"wang",98.5,1234.5);
graduate *ptr=&g1;
student *pt=&s1;
pt->display();
cout<<endl;
ptr->display();
return 0;
}