一、类的继承与类的派生
继承的概念
通过已有的类建立新类的过程,叫做类的派生
。
原来的类称为基类,也称为父类或一般类;新类称为派生类,也称为子类或特殊类。
派生类的定义与大小
派生类的定义
在C++语言中,从基类派生派生类的格式
class 派生类名 : 继承方式说明符 基类名
{
类体
};
继承方式说明符指明如何控制基类成员在派生类中的访问属性,通常有public、private和protected 3中方式。
例5-1 基类与派生类的定义
class BaseClass //基类
{
int v1,v2;
};
class DerivedClass:public BaseClass //派生类
{
int v3;
};
空类也可以作为基类。
程序5-1 派生类该百年基类成员的访问权限
#include <iostream>
using namespace std;
class BaseClass
{
public:
int v1,v2;
BaseClass():v1(1),v2(1){}
int temp1(){}
};
class DerivedClass:public BaseClass
{
int v1;
int temp1(){}
public:
DerivedClass():v1(10){}
void printv()
{
cout<<"v1="<<v1<<endl;
cout<<"Base.v1="<<BaseClass::v1<<endl;
}
};
int main()
{
BaseClass bc;
DerivedClass dc;
dc.printv();
return 0;
}
v1=10
Base.v1=1
类的大小
派生类大小占用的存储空间大小,等于基类成员变量占用的存储空间大小加上派生类大小自身成员变量占用的存储空间大小。可以使用sizeof()函数计算大小占用的字节数。
为类大小分配空间时,遵循字节对齐的原则,空类的大小是1,这是一种特殊情况。
对象占用的存储空间包含对象中各成员变量占用的存储空间。对象的大小与普通成员变量有关,与成员函数和类中的静态成员变量无关,即普通成员函数、静态成员函数、静态成员变量、静态常量成员变量等均对类对象的大小没有影响。
程序5-2 基类与子类占用空间及字节对齐
#include <iostream>
using namespace std;
class BaseClass
{
int v1,v2;
char v4;
public:
int temp1(){}
};
class DerivedClass:public BaseClass
{
int v3;
int *p;
public:
int temp(){}
};
int main()
{
cout<<"Base="<<sizeof(BaseClass)<<endl;
cout<<"Derived="<<sizeof(DerivedClass)<<endl;
return 0;
}
Base=12
Derived=24
继承关系的特殊性
如果基类有友元类或友元函数,则其派生类不会因继承关系而也有此友元类或友元函数。
如果基类是某类的友元,则这种友元关系是被继承的。即被派生类继承过来的成员函数。
如果原来是基类的友元函数,那么它作为派生类的成员函数仍然是某类的友元函数。
基类的友元不一定是派生类的友元;基类的成员函数是某类的友元函数,则其作为派生类继承的成员函数仍是某类的友元函数。
程序5-3 派生类继承了友元函数
#include <iostream>
using namespace std;
class another; //前向引用声明
class Base //基类
{
private:
float x;
public:
void print(const another &K);
};
class Derived:public Base //派生类
{
private:
float y;
};
class another
{
private:
int aaa;
public:
another()
{
aaa = 100;
}
friend void Base::print(const another &K);
};
void Base::print(const another &K)
{
cout<<"Base:"<<K.aaa<<endl;
}
int main()
{
Base a;
Derived d;
another ano;
a.print(ano);
d.print(ano);
return 0;
}
Base:100
Base:100
如果派生类Derived中重写了print()函数,若还想在函数中访问another的私有成员,则必须将类Derived的print()函数也声明为another的友元。
程序5-4 派生关系中的静态成员
#include <iostream>
using namespace std;
class Base
{
private:
float x;
public:
static int staV;
Base()
{
staV++;
}
};
int Base::staV = 0;
class Derived:public Base
{
private:
float y;
public:
Derived()
{
staV++;
}
};
int main()
{
Base a;
cout<<a.staV<<endl;
Derived d;
cout<<d.staV<<endl;
return 0;
}
1
3
有继承关系的类之间的访问
在类的派生层次结构中,基类的成员和派生类新增的成员都具有类作用域。二者的作用范围不同,是相互包含的两个层,派生类在内层,基类在外层。
程序5-5 访问基类和派生类成员的方式
#include <iostream>
using namespace std;
class CB
{
public:
int a;
CB(int x)
{
a = x;
}
void showa()
{
cout<<"Class CB--a="<<a<<endl;
}
};
class CD:public CB
{
public:
int a;
CD(int x,int y):CB(x)
{
a = y;
}
void showa()
{
cout<<"Class CD--a="<<a<<endl;
}
void print2a()
{
cout<<"a="<<a<<endl;
cout<<"CB:a="<<CB::a<<endl;
}
};
int main()
{
CB CBobj(12);
CBobj.showa();
CD CDobj(48,999);
CDobj.showa();
CDobj.CB::showa();
cout<<"CDobj.a="<<CDobj.a<<endl;
cout<<"CDobj.CB::a="<<CDobj.CB::a<<endl;
}
Class CB--a=12
Class CD--a=999
Class CB--a=48
CDobj.a=999
CDobj.CB::a=48
程序5-6 类之间的访问示例
#include <iostream>
#include <string>
using namespace std;
class CStudent
{
private:
string name; //姓名
string id; //学号
char gender; //性别,'F'代表女生,'M'代表男生
int age; //年龄
public:
void PrintInfo();
void SetInfo(const string &,const string &,int,char);
void SetName(string);
string GetName();
void SetId(string);
string GetId();
};
void CStudent::PrintInfo()
{
cout<<"姓名:\t"<<name<<endl;
cout<<"学号:\t"<<id<<endl;
cout<<"年龄:\t"<<age<<endl;
cout<<"性别:\t"<<gender<<endl;
}
void CStudent::SetInfo(const string & name_,const string & id_,int age_,char gender_)
{
name = name_;
id = id_;
age = age_;
gender = gender_;
}
void CStudent::SetName(string name)
{
this->name = name;
}
string CStudent::GetName()
{
return name;
}
void CStudent::SetId(string id)
{
this->id = id;
}
string CStudent::GetId(){
return id;
}
class CUndergraduateStudent:public CStudent //本科生类,继承于类CStudent
{
private:
string department; //学生所属的系名
public:
void PrintInfo();
void SetInfo(const string &,const string &,int,char,const string &);
};
void CUndergraduateStudent::PrintInfo()
{
CStudent::PrintInfo(); //调用基类的公有PrintInfo函数
cout<<"院系:\t"<<department<<endl<<endl; //访问的是本派生类的成员变量
}
void CUndergraduateStudent::SetInfo(const string & name_,const string & id_,int age_,char gender_,const string & department_)
{
CStudent::SetInfo(name_,id_,age_,gender_); //调用基类的公有SetInfo函数
department = department_;
}
class CGraduatedStudent:public CStudent //研究生类,继承于类CStudent
{
private:
string department;
string advisor; //导师
public:
void PrintInfo();
void SetInfo(const string & name_,const string & id_,int age_,char gender_,const string & department_,const string & advisor_);
};
void CGraduatedStudent::PrintInfo()
{
CStudent::PrintInfo();
cout<<"院系:\t"<<department<<endl;
cout<<"导师:\t"<<advisor<<endl<<endl;
}
void CGraduatedStudent::SetInfo(const string & name_,const string & id_,int age_,char gender_,const string & department_,const string & advisor_)
{
CStudent::SetInfo(name_,id_,age_,gender_);
department = department_;
advisor = advisor_;
}
int main()
{
CStudent s1;
CUndergraduateStudent s2;
CGraduatedStudent s3;
s2.SetInfo("小张","2018-6-2-4",19,'M',"数学系");
s3.SetInfo("小李","M2017-9",23,'F',"计算机学院","孟教授");
s2.PrintInfo();
s3.PrintInfo();
// cout<<s2.name<<endl;
// s2.name = "张一";
cout<<s2.GetName()<<endl;
s2.SetName("张一");
s2.PrintInfo();
cout<<s2.GetName()<<","<<s2.GetId()<<endl;
return 0;
}
姓名: 小张
学号: 2018-6-2-4
年龄: 19
性别: M
院系: 数学系
姓名: 小李
学号: M2017-9
年龄: 23
性别: F
院系: 计算机学院
导师: 孟教授
小张
姓名: 张一
学号: 2018-6-2-4
年龄: 19
性别: M
院系: 数学系
张一,2018-6-2-4
程序5-7 类之间的访问
假设公司雇员分为雇员(employee)、经理(manager)、工程师(engineer)和高级主管(director)。各类的属性如下:
employee(雇员)类:姓名、年龄、工资;
manager(经理)类:姓名、年龄、工资、行政级别;
engineer(工程师)类:姓名、年龄、工资、专业、学位:
director(高级主管)类:姓名、年龄、工资、行政级别、职务。
将类employee 设计为基类,含有成员变量:姓名、年龄和工资。类manager 和类 engineer 是类employee的派生类,类director是类manager的派生类。
#include <iostream>
#include <string>
using namespace std;
class employee //类employee将作为其他几个类的基类
{
short age;
float salary;
protected:
string name;
public:
employee(short ag,float sa,string na)
{
age = ag;
salary = sa;
name = na;
};
void print()
{
cout<<"\n"<<name<<":\t";
cout<<age<<":\t";
cout<<salary;
}
~employee(){}
};
class manager:public employee //派生类
{
int level;
public:
manager(short ag,float sa,string na,int lev):employee(ag,sa,na) //对基类初始化
{
level = lev;
}
void print()
{
employee::print();
cout<<"\tlevel:"<<level;
}
};
class engineer:public employee
{
char speciality,adegree;
public:
engineer(short ag,float sa,string na,char spe,char ade):employee(ag,sa,na)
{
speciality = spe;
adegree = ade;
}
void print()
{
employee::print();
cout<<"\tspeciality:"<<speciality;
cout<<"\tadegree:"<<adegree;
}
};
enum ptitle {PS,GM,VPS,VGM};
class director:public manager
{
ptitle post;
public:
director(short ag,float sa,string na,int le,ptitle po):manager(ag,sa,na,le)
{
post = po;
}
void print()
{
manager::print();
cout<<"\tpost:"<<post<<endl;
}
};
int main()
{
employee emp1(23,610.5,"wang"),emp2(27,824.75,"li");
manager man1(32,812.45,"zhang",11),man2(34,1200.5,"chen",7);
engineer eng(26,1420.10,"sun",'E','M');
director dir(38,1800.2,"liu",2,VPS);
emp1.print();
emp2.print();
man1.print();
man2.employee::print();
eng.print();
dir.print();
}
wang: 23: 610.5
li: 27: 824.75
zhang: 32: 812.45 level:11
chen: 34: 1200.5
sun: 26: 1420.1 speciality:E adegree:M
liu: 38: 1800.2 level:2 post:2
protected访问范围说明符
基类中的保护成员可以在派生类的成员函数中被访问。
程序5-8 基类中的成员是私有成员时的访问方式
#include <iostream>
using namespace std;
class BaseClass
{
int v1,v2; //私有成员变量
public:
void SetValue(int m,int n)
{
v1 = m;
v2 = n;
}
void PrintValue();
};
void BaseClass::PrintValue()
{
cout<<"v1="<<v1<<"\tv2="<<v2;
}
class DerivedClass:public BaseClass
{
int v3;
public:
void SetValue(int m,int n,int k)
{
BaseClass::SetValue(m,n);
v3 = k;
}
void PrintValue();
};
void DerivedClass::PrintValue()
{
cout<<endl;
BaseClass::PrintValue();
cout<<"\tv3="<<v3<<endl;
}
int main()
{
BaseClass baseCla;
DerivedClass derivedCla;
cout<<"初始时的随机值: "<<endl;
baseCla.PrintValue();
derivedCla.PrintValue();
cout<<"修改基类中的值后: "<<endl;
baseCla.SetValue(10,20);
baseCla.PrintValue();
derivedCla.PrintValue();
cout<<"从派生类修改从基类继承的值及本类的值: "<<endl;
derivedCla.SetValue(100,200,300);
baseCla.PrintValue();
derivedCla.PrintValue();
return 0;
}
初始时的随机值:
v1=0 v2=0
v1=0 v2=0 v3=19
修改基类中的值后:
v1=10 v2=20
v1=0 v2=0 v3=19
从派生类修改从基类继承的值及本类的值:
v1=10 v2=20
v1=100 v2=200 v3=300
程序5-9 基类中保护成员的访问方式
#include <iostream>
using namespace std;
class BaseClass
{
protected:
int v1,v2;
public:
void SetValue(int m,int n)
{
v1 = m;
v2 = n;
}
void PrintValue();
};
void BaseClass::PrintValue()
{
cout<<"v1="<<v1<<"\tv2="<<v2;
}
class DerivedClass:public BaseClass
{
int v3;
public:
void SetValue(int m,int n,int k)
{
v1 = m;
v2 = n;
v3 = k;
}
void SetValue(int m,int n)
{
v1 = m;
v2 =n;
}
void PrintValue();
};
void DerivedClass::PrintValue()
{
cout<<endl;
BaseClass::PrintValue();
cout<<"\tv3="<<v3<<endl;
}
int main()
{
BaseClass baseCla;
DerivedClass derivedCla;
cout<<"初始时的随机值: "<<endl;
baseCla.PrintValue();
derivedCla.PrintValue();
cout<<"修改基类中的值后: "<<endl;
baseCla.SetValue(10,20);
baseCla.PrintValue();
derivedCla.PrintValue();
cout<<"从派生类修改从基类继承的值及本类的值: "<<endl;
derivedCla.SetValue(100,200,300);
baseCla.PrintValue();
derivedCla.PrintValue();
return 0;
}
初始时的随机值:
v1=0 v2=0
v1=0 v2=0 v3=19
修改基类中的值后:
v1=10 v2=20
v1=0 v2=0 v3=19
从派生类修改从基类继承的值及本类的值:
v1=10 v2=20
v1=100 v2=200 v3=300
多重继承
C++允许从多个类派生一个类,即一个派生类可以同时有多个基类。这称为多重继承。
从一个基类派生一个派生类的情况,称为单继承或单重继承。
一个类从多个基类派生的格式
class 派生类名 : 继承方式说明符 基类名1,继承方式说明符 基类名2,...,继承方式说明符 基类名n
{
类体
};
程序5-10 多重继承下的访问方式
#include <iostream>
using namespace std;
class CB1
{
public:
int a; //重名
CB1(int x)
{
a = x;
}
void showa() //重名
{
cout<<"Class CB1==>a="<<a<<endl;
}
};
class CB2
{
public:
int a; //重名
CB2(int x)
{
a = x;
}
void showa() //重名
{
cout<<"Class CB2==>a="<<a<<endl;
}
};
class CD : public CB1,public CB2 //多重继承,两个基类
{
public:
int a; //与两个基类成员变量a重名
CD(int x,int y,int z):CB1(x),CB2(y)
{
a = z;
}
void showa() //与两个基类成员函数showa()重名
{
cout<<"Class CD==>a="<<a<<endl;
}
void print3a()
{
cout<<"a="<<a<<endl;
cout<<"CB1::a="<<CB1::a<<endl;
cout<<"CB2::a="<<CB2::a<<endl;
}
};
int main()
{
CB1 CB1obj(11);
CB1obj.showa();
CD CDobj(101,202,909);
CDobj.showa();
CDobj.CB1::showa();
cout<<"CDobj.a="<<CDobj.a<<endl;
cout<<"CDobj.CB2::a="<<CDobj.CB2::a<<endl;
}
Class CB1==>a=11
Class CD==>a=909
Class CB1==>a=101
CDobj.a=909
CDobj.CB2::a=202
多重继承方式下,为避免二义性,成员变量的访问方式如下
程序5-11 多重继承
#include <iostream>
using namespace std;
class BaseClass1
{
public:
int v1,v2;
BaseClass1();
BaseClass1(int,int);
~BaseClass1();
};
BaseClass1::BaseClass1()
{
cout<<"BaseClass1 无参构造函数"<<endl;
}
BaseClass1::BaseClass1(int m,int n):v1(m),v2(n)
{
cout<<"BaseClass1 带2个参数构造函数"<<endl;
}
BaseClass1::~BaseClass1()
{
cout<<"BaseClass1 析构函数"<<endl;
}
class BaseClass2
{
public:
int v1,v4;
BaseClass2();
BaseClass2(int,int);
~BaseClass2();
};
BaseClass2::BaseClass2()
{
cout<<"BaseClass2 无参构造函数"<<endl;
}
BaseClass2::BaseClass2(int m,int n):v1(m),v4(n)
{
cout<<"BaseClass2 带2个参数构造函数"<<endl;
}
BaseClass2::~BaseClass2()
{
cout<<"BaseClass2 析构函数"<<endl;
}
class DerivedClass:public BaseClass1,public BaseClass2
{
public:
int v3;
public:
DerivedClass();
DerivedClass(int);
DerivedClass(int,int,int,int);
~DerivedClass();
};
DerivedClass::DerivedClass()
{
cout<<"DerivedClass 无参构造函数"<<endl;
}
DerivedClass::DerivedClass(int k):v3(k)
{
cout<<"DerivedClass 带1个参数的构造函数"<<endl;
}
DerivedClass::DerivedClass(int m,int n,int k,int t):BaseClass1(m,n),BaseClass2(m,t),v3(k)
{
cout<<"DerivedClass 带4个参数的构造函数"<<endl;
}
DerivedClass::~DerivedClass()
{
cout<<"DerivedClass 析构函数"<<endl;
}
int main()
{
cout<<"带参数对象的创建"<<endl;
DerivedClass derivedCla1(1000,2000,3000,4000);
cout<<"v1="<<derivedCla1.BaseClass1::v1<<endl;
cout<<"v2="<<derivedCla1.v2<<endl;
cout<<"v1="<<derivedCla1.BaseClass2::v1<<endl;
cout<<"v4="<<derivedCla1.v4<<endl;
cout<<"v3="<<derivedCla1.v3<<endl;
return 0;
}
带参数对象的创建
BaseClass1 带2个参数构造函数
BaseClass2 带2个参数构造函数
DerivedClass 带4个参数的构造函数
v1=1000
v2=2000
v1=1000
v4=4000
v3=3000
DerivedClass 析构函数
BaseClass2 析构函数
BaseClass1 析构函数
二、 访问控制
公有继承
当类的继承方式为公有继承时,基类的公有成员在派生类中直接访问,在基类与派生类外直接访问;基类的保护成员在派生类中直接访问,在基类与派生类外调用公有函数访问;基类的私有成员在派生类中调用公有函数访问,在基类与派生类外调用公有函数访问。
当类的继承方式为公有继承时,派生类中定义的公有成员在派生类中直接访问.在基类与派生类外直接访问;派生类中定义的保护成员在派生类中直接访问,在基类与派生类外调用公有函数访问;派生类中定义的私有成员在派生类中直接访问,在基类与派生类外调用公有函数访问。
表5-1 公有继承的访问控制
各成员 | 派生类中 | 基类与派生类外 |
---|---|---|
基类的公有成员 | 直接访问 | 直接访问 |
基类的保护成员 | 直接访问 | 调用公有函数访问 |
基类的私有成员 | 调用公有函数访问 | 调用公有函数访问 |
从基类继承的公有成员 | 直接访问 | 直接访问 |
从基类继承的保护成员 | 直接访问 | 调用公有函数访问 |
从基类继承的私有成员 | 调用公有函数访问 | 调用公有函数访问 |
派生类中定义的公有成员 | 直接访问 | 直接访问 |
派生类中定义的保护成员 | 直接访问 | 调用公有函数访问 |
派生类中定义的私有成员 | 直接访问 | 调用公有函数访问 |
程序5-12 公有继承访问控制示例
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
int vBPub;
protected:
int vBPro;
private:
int vBPri;
public:
Base()
{
vBPub = 10;
vBPro = 20;
vBPri = 30;
};
void SetPriValue(int);
void SetProValue(int,int);
int GetPriValue();
int GetProValue();
};
void Base::SetPriValue(int k)
{
vBPri = k;
}
void Base::SetProValue(int m,int n)
{
vBPro = m;
vBPri = n;
}
int Base::GetPriValue()
{
return vBPri;
}
int Base::GetProValue()
{
return vBPro;
}
class Derived:public Base
{
public:
int vDPub,vBPub;
protected:
int vDPro;
private:
int vDPri;
public:
Derived()
{
vDPub = 100;
vDPro = 200;
vDPri = 300;
vBPub = 15;
};
void SetPriValue(int);
void SetProValue(int,int);
int GetPriValue();
int GetProValue();
void PrintValue();
};
void Derived::SetPriValue(int k)
{
vDPri = k;
}
void Derived::SetProValue(int m,int n)
{
vDPro = m;
vDPri = n;
Base::vBPro = 2*m;
// Base::vBPri = 2*n; //不可以直接访问从基类继承的私有成员变量
}
int Derived::GetPriValue()
{
return vDPri;
}
int Derived::GetProValue()
{
return vDPro;
}
void Derived::PrintValue()
{
cout<<"在派生类中访问基类"<<endl;
cout<<"基类公有变量: "<<vBPub<<endl; //派生类可以访问基类的公有成员变量
cout<<"基类保护变量: "<<vBPro<<endl; //派生类可以访问基类的保护成员变量
// cout<<Base::vBPri<<endl; //不能直接访问基类的私有变量
cout<<"基类私有变量: "<<Base::GetPriValue()<<endl; //通过函数访问
cout<<"在派生类中访问派生类"<<endl;
cout<<"派生类公有变量: "<<vDPub<<endl;
cout<<"派生类保护变量: "<<vDPro<<endl;
cout<<"派生类私有变量: "<<vDPri<<endl;
cout<<"从基类继承的公有变量: "<<Base::vBPub<<endl;
cout<<"从基类继承的保护变量: "<<Base::vBPro<<endl;
// cout<<"从基类继承的私有变量: "<<Base::vBPri<<endl; //不可以直接访问
}
int main()
{
Base bObj;
Derived dObj;
cout<<"在主函数中访问基类"<<endl;
cout<<"公有成员: "<<bObj.vBPub<<endl;
// cout<<"保护成员: "<<bObj.vBPro<<endl; //不能直接访问保护成员变量
cout<<"保护成员: "<<bObj.GetProValue()<<endl; //通过函数访问
cout<<"私有成员: "<<bObj.GetPriValue()<<endl;
cout<<"在主函数中访问派生类"<<endl;
cout<<"公有成员: "<<dObj.vDPub<<endl;
// cout<<"保护成员: "<<dObj.vDpro<<endl; //不能直接访问保护成员变量
cout<<"保护成员: "<<dObj.GetProValue()<<endl;
cout<<"基类 保护成员: "<<dObj.Base::GetProValue()<<endl;
cout<<"私有成员: "<<dObj.GetPriValue()<<endl;
cout<<"dObj.vBPub="<<dObj.vBPub<<endl;
cout<<"dObj.Base::vBpub="<<dObj.Base::vBPub<<endl;
dObj.PrintValue();
return 0;
}
在主函数中访问基类
公有成员: 10
保护成员: 20
私有成员: 30
在主函数中访问派生类
公有成员: 100
保护成员: 200
基类 保护成员: 20
私有成员: 300
dObj.vBPub=15
dObj.Base::vBpub=10
在派生类中访问基类
基类公有变量: 15
基类保护变量: 20
基类私有变量: 30
在派生类中访问派生类
派生类公有变量: 100
派生类保护变量: 200
派生类私有变量: 300
从基类继承的公有变量: 10
从基类继承的保护变量: 20
类型兼容规则
类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代,也称为赋值兼容规则。
在公有派生的情况下,又以下3条类型兼容规则。
- 派生类的对象可以赋值给基类对象。
- 派生类对象可以用来初始化基类引用。
- 派生类对象的地址可以赋值给基类指针。
程序5-13 类型兼容规则的使用
#include <iostream>
using namespace std;
class A
{
int an;
public:
A(){}
A(int n)
{
an = n;
}
};
class B:public A
{
int bn;
public:
B(int n):A(2*n)
{
bn = n;
}
};
int main()
{
A a(10);
B b(20);
a = b;
return 0;
}
程序5-14 验证使用类型兼容规则的输出结果
#include <iostream>
using namespace std;
class A
{
int an;
public:
A(){}
A(int n)
{
an = n;
}
void print()
{
cout<<"A的对象:";
cout<<"an:"<<an;
}
void print(int k)
{
cout<<"an:"<<an;
}
};
class B:public A
{
int bn;
public:
B(int n):A(2*n)
{
bn = n;
}
void print()
{
cout<<"\nB的对象:";
A::print(1);
cout<<",bn="<<bn<<endl;
}
};
int main()
{
A a(10);
B b(20);
a.print();
b.print();
a = b;
a.print();
b.print();
return 0;
}
A的对象:an:10
B的对象:an:40,bn=20
A的对象:an:40
B的对象:an:40,bn=20
私有继承
当类的继承方式为私有继承时,基类的公有成员在第一级派生类中可以直接访问,在第二级派生类中不可访问,在基类与派生类外不可访问;基类的保护成员在第一级派生类中可以直接访问,在第二级派生类中不可访问,在基类与派生类外不可访向;基类的私有成员在第一级派生类中可以调用公有函数访问,在第二级派生类中不可访问,在基类与派生类外不可访问。
表5-2 私有继承的访问控制
第一级派生类中 | 第二级派生类中 | 基类与派生类外 | |
---|---|---|---|
基类的公有成员 | 直接访问 | 不可访问 | 不可访问 |
基类的保护成员 | 直接访问 | 不可访问 | 不可访问 |
基类的私有成员 | 调用公有函数访问 | 不可访问 | 不可访问 |
保护继承
保护继承中,基类的公有成员和保护成员都以保护成员的身份出现在派生类中,而基类的私有成员不可以直接访问。这样,派生类的其他成员可以直接访问从基类继承来的公有和保护成员,但在类外通过派生类的对象无法直接访问他们。
三、派生类的构造函数和析构函数
构造函数与析构函数
在执行一个派生类的构造函数之前,总是先执行基类的构造函数。派生类对象消亡时,先执行派生类的析构函数,在执行基类的析构函数。
如果基类定义了带有形参表的构造函数,则派生类就应当定义构造函数。
定义派生类构造函数的格式
派生类名::派生类名(参数表):基类名1(基类1 初始化参数表),...,基类名m(基类m初始化参数表),成员对象名1(成员对象1 初始化参数表),...,成员对象名n(成员对象n 初始化参数表)
{
派生类构造函数函数体 //其他初始化操作
}
程序5-15 基类和派生类的构造函数与析构函数
#include <iostream>
using namespace std;
class BaseClass
{
protected:
int v1,v2;
public:
BaseClass();
BaseClass(int,int);
~BaseClass();
};
BaseClass::BaseClass()
{
cout<<"BaseClass 无参构造函数"<<endl;
}
BaseClass::BaseClass(int m,int n)
{
v1 = m;
v2 = n;
cout<<"BaseClass 2个参数构造函数"<<endl;
}
BaseClass::~BaseClass()
{
cout<<"BaseClass 析构函数"<<endl;
}
class DerivedClass:public BaseClass
{
int v3;
public:
DerivedClass();
DerivedClass(int);
DerivedClass(int,int,int);
~DerivedClass();
};
DerivedClass::DerivedClass()
{
cout<<"DerivedClass 无参构造函数"<<endl;
}
DerivedClass::DerivedClass(int k):v3(k)
{
cout<<"DerivedClass 带1个参数的构造函数"<<endl;
}
DerivedClass::DerivedClass(int m,int n,int k):BaseClass(m,n),v3(k)
{
cout<<"DerivedClass 带3个参数的构造函数"<<endl;
}
DerivedClass::~DerivedClass()
{
cout<<"DerivedClass 析构函数"<<endl;
}
int main()
{
cout<<"无参对象的创建"<<endl;
BaseClass baseCla;
DerivedClass derivedCla;
return 0;
}
无参对象的创建
BaseClass 无参构造函数
BaseClass 无参构造函数
DerivedClass 无参构造函数
DerivedClass 析构函数
BaseClass 析构函数
BaseClass 析构函数
例5-11 修改程序5-15
将主函数修改如下:
int main()
{
cout<<"带参数对象的创建"<<endl;
BaseClass baseCla1(10,20);
DerivedClass derivedCla1(30);
return 0;
}
带参数对象的创建
BaseClass 2个参数构造函数
BaseClass 无参构造函数
DerivedClass 带1个参数的构造函数
DerivedClass 析构函数
BaseClass 析构函数
BaseClass 析构函数
例5-12
int main()
{
cout<<"无参对象的创建"<<endl;
BaseClass baseCla;
DerivedClass derivedCla;
cout<<"带参数对象的创建"<<endl;
BaseClass baseCla1(10,20);
DerivedClass derivedCla1(30);
return 0;
}
无参对象的创建
BaseClass 无参构造函数
BaseClass 无参构造函数
DerivedClass 无参构造函数
带参数对象的创建
BaseClass 2个参数构造函数
BaseClass 无参构造函数
DerivedClass 带1个参数的构造函数
DerivedClass 析构函数
BaseClass 析构函数
BaseClass 析构函数
DerivedClass 析构函数
BaseClass 析构函数
BaseClass 析构函数
程序5-16 调用基类和派生类的构造函数、析构函数和成员函数
#include <iostream>
using namespace std;
class Base
{
private:
int Y;
public:
Base(int y = 0)
{
Y = y;
cout<<"Base("<<y<<")"<<endl;
}
~Base()
{
cout<<"~Base()"<<endl;
}
void print()
{
cout<<Y<<" ";
}
};
class Derived:public Base
{
private:
int Z;
public:
Derived(int y,int z):Base(y)
{
Z = z;
cout<<"Derived("<<y<<","<<z<<")"<<endl;
}
~Derived()
{
cout<<"~Derived()"<<endl;
}
void print()
{
Base::print();
cout<<Z<<endl;
}
};
int main()
{
Derived d(10,20);
d.print();
return 0;
}
Base(10)
Derived(10,20)
10 20
~Derived()
~Base()
复制构造函数
程序5-17 派生类中的复制构造函数
#include <iostream>
using namespace std;
class A
{
public:
A()
{
i = 100;
cout<<"类A默认构造函数"<<endl;
}
A(const A &s)
{
i = s.i;
cout<<"类A复制构造函数"<<endl;
}
int getValue();
void setValue(int);
private:
int i;
};
int A::getValue()
{
return i;
}
void A::setValue(int k)
{
i = k;
}
class B:public A //公有派生类
{
private:
float f;
public:
B()
{
f = 20.1;
cout<<"类B默认构造函数"<<endl;
}
B(const B &v):A::A(v),f(v.f)
{
cout<<"类B复制构造函数"<<endl;
}
float getValue();
int getValue1()
{
return A::getValue();
}
};
float B::getValue()
{
return f;
}
int main()
{
A a;
B b;
B bb(b);
return 0;
}
类A默认构造函数
类A默认构造函数
类B默认构造函数
类A复制构造函数
类B复制构造函数
程序5-18 赋值运算符的重载及使用
#include <iostream>
using namespace std;
class CBase
{
public:
CBase(){}
CBase(CBase & c)
{
cout<<"CBase::复制构造函数"<<endl;
}
CBase & operator=(const CBase &b)
{
cout<<"CBase::operator="<<endl;
return *this;
}
};
class CDerived:public CBase
{
public:
CDerived()
{
cout<<"CDerived::复制构造函数"<<endl;
}
};
int main()
{
CDerived d1,d2;
CDerived d3(d1); //d3初始化过程中会调用类CBase的复制构造函数
d2 = d1; //会调用类CBase重载的“=”运算符
return 0;
}
CDerived::复制构造函数
CDerived::复制构造函数
CBase::复制构造函数
CBase::operator=
多重继承的构造函数与析构函数
设计两个基类BaseClass1和BaseClass2共同派生一个派生类DerivedClass。
程序5-19 多重继承
#include <iostream>
using namespace std;
class BaseClass1
{
protected:
int v1,v2;
public:
BaseClass1();
BaseClass1(int,int);
~BaseClass1();
void SetValue(int,int);
void PrintValue();
};
BaseClass1::BaseClass1():v1(0),v2(0)
{
cout<<"BaseClass1 无参构造函数"<<endl;
}
BaseClass1::BaseClass1(int m,int n)
{
v1 = m;
v2 = n;
cout<<"BaseClass1 带2个参数构造函数"<<endl;
}
BaseClass1::~BaseClass1()
{
cout<<"BaseClass1 析构函数"<<endl;
}
void BaseClass1::SetValue(int m,int n)
{
v1 = m;
v2 = n;
}
void BaseClass1::PrintValue()
{
cout<<"v1="<<v1<<"\tv2="<<v2;
}
class BaseClass2
{
protected:
int v1,v4;
public:
BaseClass2();
BaseClass2(int,int);
~BaseClass2();
void SetValue(int,int);
void PrintValue();
};
BaseClass2::BaseClass2():v1(1),v4(1)
{
cout<<"BaseClass2 无参构造函数"<<endl;
}
BaseClass2::BaseClass2(int m,int n)
{
v1 = m;
v4 = n;
cout<<"BaseClass2 带2个参数构造函数"<<endl;
}
BaseClass2::~BaseClass2()
{
cout<<"BaseClass2 析构函数"<<endl;
}
void BaseClass2::SetValue(int m,int n)
{
v1 = m;
v4 = n;
}
void BaseClass2::PrintValue()
{
cout<<"v1="<<v1<<"\tv4="<<v4;
}
class DerivedClass :public BaseClass1,public BaseClass2
{
public:
int v3;
public:
DerivedClass();
DerivedClass(int);
DerivedClass(int,int,int,int);
~DerivedClass();
void SetValue(int m,int n,int k,int h)
{
BaseClass1::SetValue(m,n);
BaseClass2::SetValue(2*m,h);
v3 = k;
}
void SetValue(int m,int n,int k)
{
BaseClass1::SetValue(m,n);
BaseClass2::SetValue(2*m,2*n);
v3 = k;
}
void SetValue(int m,int n)
{
BaseClass1::SetValue(m,n);
BaseClass2::SetValue(-1,-1);
v3 = -1;
}
void PrintValue();
};
DerivedClass::DerivedClass():BaseClass1(0,0),BaseClass2(0,0),v3(0)
{
cout<<"DerivedClass 无参构造函数"<<endl;
}
DerivedClass::DerivedClass(int k)
{
v3 = k;
cout<<"DerivedClass 带1个参数的构造函数"<<endl;
}
DerivedClass::DerivedClass(int m,int n,int k,int t):BaseClass1(m,n),BaseClass2(m,t),v3(k)
{
cout<<"DerivedClass 带4个参数的构造函数"<<endl;
}
DerivedClass::~DerivedClass()
{
cout<<"DerivedClass 析构函数"<<endl;
}
void DerivedClass::PrintValue()
{
BaseClass1::PrintValue();
cout<<"\tv3="<<v3<<endl;
BaseClass2::PrintValue();
cout<<endl;
}
int main()
{
cout<<"带参数对象的创建"<<endl;
DerivedClass derivedCla1(1000,2000,3000,4000);
derivedCla1.PrintValue();
return 0;
}
带参数对象的创建
BaseClass1 带2个参数构造函数
BaseClass2 带2个参数构造函数
DerivedClass 带4个参数的构造函数
v1=1000 v2=2000 v3=3000
v1=1000 v4=4000
DerivedClass 析构函数
BaseClass2 析构函数
BaseClass1 析构函数
四、类之间的关系
类与类之间的关系
使用已有类编写新的类有两种方式:继承和组合。
封闭类的派生
如果一个类的成员变量是另一个类的对象,则为封闭类。
定义封闭类构造函数的形式
类名::类名(形参表):内嵌对象1(形参表),内嵌对象2(形参表),...
{
类体
}
程序5-20 封闭类的构造函数
#include <iostream>
#include <string>
using namespace std;
class myDate
{
public:
myDate(); //构造函数
myDate(int);
myDate(int,int);
myDate(int,int,int); //构造函数
myDate(const myDate &d);
~myDate();
void setDate(int,int,int); //设置日期
void setDate(myDate); //设置日期
myDate getDate(); //获取日期
void setYear(int); //设置年
int getMonth();//获取月
void printDate() const; //打印日期
private:
int year,month,day; //成员变量,表示年、月、日
};
//在类体外定义成员函数
myDate::myDate()
{
year = 1970;
month = 1;
day = 1;
cout<<"myDate默认构造函数"<<endl;
}
myDate::myDate(int y)
{
year = y;
month = 1;
day = 1;
cout<<"myDate 带1个参数的构造函数"<<endl;
}
myDate::myDate(int y,int m)
{
year = y;
month = m;
day = 1;
cout<<"myDate 带2个参数的构造函数"<<endl;
}
myDate::myDate(int y,int m,int d)
{
year = y;
month = m;
day = d;
cout<<"myDate 带3个参数的构造函数"<<endl;
}
myDate::myDate(const myDate &d)
{
year = d.year;
month = d.month;
day = d.day;
cout<<"myDate_COPY 构造函数"<<endl;
}
myDate::~myDate()
{
cout<<"myDate 析构函数"<<endl;
}
void myDate::setDate(int y,int m,int d)
{
year=y;
month=m;
day=d;
return;
}
void myDate::setDate(myDate oneD)
{
year=oneD.year;
month=oneD.month;
day=oneD.day;
return;
}
myDate myDate::getDate()
{
myDate d;
d.year = year;
d.month = month;
d.day = day;
return d;
}
void myDate::setYear(int y)
{
year = y;
return;
}
int myDate::getMonth()
{
return month;
}
void myDate::printDate() const
{
cout<<year<<"/"<<month<<"/"<<day;
return;
}
class CStudent
{
public:
CStudent();
CStudent(string,myDate);
~CStudent();
void setStudent(string,myDate); //设置学生信息
void setName(string); //设置学生姓名
string getName(); //获取姓名
void setBirthday(myDate); //设置生日
myDate getBirthday(); //获取生日
void printInfo() const; //打印信息
private:
string name; //姓名
myDate birthday; //生日
};
CStudent::CStudent():name("Noname"),birthday(myDate())
{
cout<<"CStudent 默认构造函数"<<endl;
}
CStudent::CStudent(string str,myDate d):name(str),birthday(d)
{
cout<<"CStudent 有参构造函数"<<endl;
}
CStudent::~CStudent()
{
cout<<"CStudent 析构函数"<<endl;
}
void CStudent::setStudent(string s,myDate d)
{
name = s;
birthday.setDate(d);
return;
}
void CStudent::setName(string n)
{
name = n;
return;
}
string CStudent::getName()
{
return name;
}
void CStudent::setBirthday(myDate d)
{
birthday.setDate(d);
return;
}
myDate CStudent::getBirthday()
{
return birthday;
}
void CStudent::printInfo()const
{
cout<<"姓名: "<<name<<"\t生日: ";
birthday.printDate(); //调用类myDate的成员函数
cout<<endl;
}
class CUndergraduateStudent:public CStudent //本科生类,继承于 CStudent
{
private:
string department; //学生所属的系名
public:
CUndergraduateStudent();
CUndergraduateStudent(string,myDate);
~CUndergraduateStudent();
void setDep(string);
void PrintInfo();
};
CUndergraduateStudent::CUndergraduateStudent()
{
cout<<"CUndergraduateStudent 默认构造函数"<<endl;
}
CUndergraduateStudent::CUndergraduateStudent(string str,myDate d):CStudent(str,d)
{
cout<<"CUndergraduateStudent 有参构造函数"<<endl;
}
CUndergraduateStudent::~CUndergraduateStudent()
{
cout<<"CUndergraduateStudent 析构函数"<<endl;
}
void CUndergraduateStudent::setDep(string dep)
{
department = dep;
}
void CUndergraduateStudent::PrintInfo()
{
CStudent::printInfo(); //调用基类的printInfo函数
cout<<"院系:\t"<<department<<endl<<endl;
}
int main()
{
CUndergraduateStudent s2("小张",myDate());
return 0;
}
myDate默认构造函数
myDate_COPY 构造函数
myDate_COPY 构造函数
CStudent 有参构造函数
myDate 析构函数
CUndergraduateStudent 有参构造函数
myDate 析构函数
CUndergraduateStudent 析构函数
CStudent 析构函数
myDate 析构函数
*互包含关系的类
在处理相对复杂的问题而需要考虑类的组合时,很可能遇到两个类相互引用的情况,称为循环依赖。
class A //类A的定义
{
public:
void f(B b); //以类B对象为形参的成员函数
};
class B //类B的定义
{
public:
void g(A a); //以类A对象为形参的成员函数
}
解决办法是使用前向引用声明。
在提供一个完整的类定义之前,不能定义该类的对象,也不能在内联成员函数中使用该类的对象。
例5-15 互包含的类
#include <iostream>
#include <string>
using namespace std;
class B; //前向引用声明
class A
{
int a;
B b; //不完整的类型定义
};
class B
{
A a;
int b;
};
int main()
{
return 0;
}
程序5-21 互包含的类
#include <iostream>
#include <string>
using namespace std;
class B;
class A
{
public:
int aInt;
B *bPoint = NULL;
void SetValue(int v)
{
aInt = v;
}
};
class B
{
public:
A aCla;
int bInt;
void SetValue(int v)
{
bInt = v;
}
};
int main()
{
A ca;
B cb;
ca.bPoint = &cb;
cout<<"ca.bPoint="<<ca.bPoint<<endl;
cout<<"cb Addr="<<&cb<<endl;
cout<<"ca.aInt="<<ca.aInt<<endl;
cout<<"cb.aCLa.aInt="<<cb.aCla.aInt<<endl;
cout<<"cb.bInt="<<cb.bInt<<endl;
cout<<"分界线"<<endl;
ca.SetValue(10);
cb.SetValue(20);
cb.aCla = ca;
cout<<"ca.bPoint="<<ca.bPoint<<endl;
cout<<"ca.aInt="<<ca.aInt<<endl;
cout<<"cb.aCla.aInt="<<cb.aCla.aInt<<endl;
cout<<"cb.bInt="<<cb.bInt<<endl;
return 0;
}
ca.bPoint=0x6ffde0
cb Addr=0x6ffde0
ca.aInt=0
cb.aCLa.aInt=-1
cb.bInt=1
分界线
ca.bPoint=0x6ffde0
ca.aInt=10
cb.aCla.aInt=10
cb.bInt=20
五、多层次的派生
在C++中,派生可以是多层次的。
在C++中,类之间的继承关系具有传递性。
在定义派生类时,只需写直接基类,不需要写间接基类。
程序5-22 多级派生时构造函数的调用
#include <iostream>
using namespace std;
class BaseClass1
{
protected:
int v1,v2;
public:
BaseClass1();
BaseClass1(int,int);
~BaseClass1();
};
BaseClass1::BaseClass1()
{
cout<<"BaseClass1 无参构造函数"<<endl;
}
BaseClass1::BaseClass1(int m,int n):v1(m),v2(n)
{
cout<<"BaseClass1 带2个参数构造函数"<<endl;
}
BaseClass1::~BaseClass1()
{
cout<<"BaseClass1 析构函数"<<endl;
}
class BaseClass2:public BaseClass1
{
protected:
int v3;
public:
BaseClass2();
BaseClass2(int);
BaseClass2(int,int,int);
~BaseClass2();
};
BaseClass2::BaseClass2()
{
cout<<"BaseClass2 无参构造函数"<<endl;
}
BaseClass2::BaseClass2(int m):v3(m)
{
cout<<"BaseClass2 带1个参数构造函数"<<endl;
}
BaseClass2::BaseClass2(int m,int n,int k):BaseClass1(m,n),v3(k)
{
cout<<"BaseClass2 带3个参数构造函数"<<endl;
}
BaseClass2::~BaseClass2()
{
cout<<"BaseClass2 析构函数"<<endl;
}
class DerivedClass:public BaseClass2
{
public:
int v4;
public:
DerivedClass();
DerivedClass(int);
DerivedClass(int,int,int,int);
~DerivedClass();
};
DerivedClass::DerivedClass()
{
cout<<"DerivedClass 无参构造函数"<<endl;
}
DerivedClass::DerivedClass(int k):v4(k)
{
cout<<"DerivedClass 带1个参数构造函数"<<endl;
}
DerivedClass::DerivedClass(int m,int n,int k,int t):BaseClass2(m,n,k),v4(t)
{
cout<<"DerivedClass 带4个参数构造函数"<<endl;
}
DerivedClass::~DerivedClass()
{
cout<<"DerivedClass 析构函数"<<endl;
}
int main()
{
cout<<"无参对象的创建"<<endl;
DerivedClass derivedCla;
return 0;
}
无参对象的创建
BaseClass1 无参构造函数
BaseClass2 无参构造函数
DerivedClass 无参构造函数
DerivedClass 析构函数
BaseClass2 析构函数
BaseClass1 析构函数
将程序5-22的主函数修改如下
int main()
{
cout<<"带参数对象的创建"<<endl;
DerivedClass derivedCla1(1000,2000,3000,4000);
return 0;
}
带参数对象的创建
BaseClass1 带2个参数构造函数
BaseClass2 带3个参数构造函数
DerivedClass 带4个参数构造函数
DerivedClass 析构函数
BaseClass2 析构函数
BaseClass1 析构函数
六、基类与派生类指针的互相转换
对于指针类型,可以使用基类指针指向派生类对象,也可以将派生类的指针直接赋值给基类指针。
但即使基类指针指向的是一个派生类的对象,也不能通过基类指针访问基类中没有而仅在派生类中定义的成员函数。
当派生类指针指向基类对象时,必须要将基类对象进行强制类型转换,才能赋给派生类指针。
程序5-23 使用指针的情况
#include <iostream>
using namespace std;
class CBase
{
protected:
int n;
public:
CBase(int i):n(i){}
void Print()
{
cout<<"CBase:n="<<n<<endl;
}
};
class CDerived:public CBase
{
public:
int v;
CDerived(int i):CBase(i),v(2*i){}
void Func(){};
void Print()
{
cout<<"CDerived:n="<<n<<endl;
cout<<"CDerived:v="<<v<<endl;
}
};
int main()
{
CDerived objDerived(3);
CBase objBase(5);
CBase *pBase = &objDerived; //使用基类指针指向派生类对象
CDerived *pDerived;
pDerived = &objDerived;
cout<<"使用派生类指针调用函数"<<endl;
pDerived->Print(); //调用的时派生类中的函数
pBase = pDerived; //基类指针 = 派生类指针,正确
cout<<"使用基类指针调用函数"<<endl;
pBase->Print(); //调用的是基类中的函数
//pBase->Func(); //错误,通过基类指针不能调用派生类函数
//pDerived = pBase //派生类指针 = 基类指针
pDerived = (CDerived *)pBase; //强制类型转换,派生类指针 = 基类指针
cout<<"使用派生类指针调用函数"<<endl;
pDerived->Print(); //调用的是派生类的函数
return 0;
}
使用派生类指针调用函数
CDerived:n=3
CDerived:v=6
使用基类指针调用函数
CBase:n=3
使用派生类指针调用函数
CDerived:n=3
CDerived:v=6
网友评论