C++类的继承
继承是C++``面向对象的三大特征之一,它使一个类可从现有类中继承,而不必从头开始定义.
类继承的实质是以旧类为基础创建新类,新类包含旧类的数据成员和成员函数,且可添加新的数据成员和成员函数.其中,旧类叫做基类或父类,新类叫做继承类或子类.
C++类的继承
继承可得到继承类,其定义形式如下:
cpp
class 继承类名 : [继承方式] 基类名
{
[访问控制修饰符:]
[成员声明列表]
}
":"符号说明这是一个继承类,":"后为继承的基类;
继承方式有3种,分别为公(公开),保护(保护)和私(私有),默认为是private;
访问控制修饰符也有3种,同样是public,protected和私,默认为是private;
成员声明列表中给出的是继承类中新增的成员变量和成员函数.
如,定义CEmployee员工类,包含3个公开成员变量,员工ID,员工姓名和所属部门:
cpp
class CEmployee
{
public:
int m_ID;
//员工`ID`
char m_Name[128];
//员工姓名
char m_Depart[128];
//所属部门
};
再定义一个COperator符号类,使其从CEmployee类继承.此时,COperator类会自动继承CEmployee类的员工ID,员工姓名和所属部门信息,同时可新增员工密码和登录方法.
cpp
class COperator : public CEmployee
{
public:
char m_Password[128];
//新增公开`成员变量``Password`,表示员工密码
bool Login();
//新增公开`成员函数``Login()`,表示登录方法
};
两个使用继承的典型场景:
待创建的类与当前类类似,只是多出若干成员变量或成员函数时,可用继承;
需要创建多个类,这些类拥有类似的成员变量或成员函数时,可用继承.将共同成员取出来,按基类定义,然后继承快速得到多个类,不但节省代码,还方便后续修改.
[实例]公开继承员工类,得到符号类,并新增成员信息,实现打卡功能.代码如下:
cpp
#include<iostream>
using namespace std;
class CEmployee
{
public:
int m_ID;
char m_Name[128];
char m_Depart[128];
CEmployee()
{
memset(m_Name, 0, 128);
memset(m_Depart, 0, 128);
}
void OutputName()
{
cout << "员工姓名:" << m_Name << endl;
}
};
class COperator : public CEmployee
{
public:
char m_Password[128];
bool Login()
{
if (strcmp(m_Name, "MR") == 0 && strcmp(m_Password, "KJ") == 0)
{
cout << "登录成功!" << endl;
return true;
}
else
{
cout << "登录失败!" << endl;
return false;
}
}
};
int main(int argc, char* argv[])
{
COperator optr;
strcpy(optr.m_Name, "MR");
strcpy(optr.m_Password, "KJ");
optr.Login();
optr.OutputName();
return 0;
}
运行程序结果为:
登录成功!
员工姓名:MR
C++继承后的可访问性
公开,私有,保护3种继承方式下,基类中的成员在继承类中的可访问性如下表所示.
表:不同继承方式下基类成员在继承类中的可访问性
继承方式 |
基类公成员 |
基类保护成员 |
基类私成员 |
|---|---|---|---|
公继承方式 |
public |
protected |
不可见 |
保护继承方式 |
protected |
protected |
不可见 |
私继承方式 |
private |
private |
不可见 |
继承方式的几点说明如下:
1),基类成员在继承类中的权限不能高于继承方式指定的权限.如:
1,继承方式为公时,基类成员在继承类中的权限将不变;
2,继承方式为保护时,基类成员在继承类中的权限最高为protected;
3,继承方式为私时,基类成员在继承类中的权限最高为私.
2)基类中的私成员在继承类中不可见,即不能在继承类的成员函数中访问或调用.因此,如果希望继承类可继承且毫无障碍地使用基类成员,可按公或保护属性声明,只有那些不希望在继承类中使用的成员才按私声明.
3)如果希望基类成员既不向外暴露(不能对象访问),还能在继承类中使用,可按保护属性声明它.基类的所有继承类可使用保护成员,这一属性将沿继承树无限向下传播.
注意,在继承类定义时可访问保护类型,不能访问用继承类声明的对象,即类体外不能访问.先来看一下公继承方式的访问示例:
cpp
class CEmployee
{
public:
void Output()
{
cout << m_ID << endl;
cout << m_Name << endl;
cout << m_Depart << endl;
}
private:
int m_ID;
char m_Name[128];
char m_Depart[128];
};
class COperator : public CEmployee
{
public:
void Output()
{
cout << m_ID << endl;
//错误,不能引用基类的私有成员`m_ID`
cout << m_Name << endl;
//错误,不能引用基类的私有成员`m_Name`
cout << m_Depart << endl;
//错误,不能引用基类的私有成员`m_Depart`
cout << m_Password << endl;
//`正确`,可引用自己的私有成员`m_Password`
}
private:
char m_Password[128];
bool Login();
};
下面是私继承方式的访问示例:
cpp
class CEmployee
{
public:
void Output()
{
cout << m_ID << endl;
cout << m_Name << endl;
cout << m_Depart << endl;
}
public:
int m_ID;
protected:
char m_Name[128];
private:
char m_Depart[128];
};
class COperator : private CEmployee
{
public:
void Output()
{
cout << m_ID << endl;
//`正确`,可引用基类的公开成员`m_ID`
cout << m_Name << endl;
//`正确`,可引用基类的保护成员`m_Name`
cout << m_Depart << endl;
//错误,不能引用基类的私有成员`m_Depart`
cout << m_Password << endl;
//`正确`,可引用自己的私有成员`m_Password`
}
private:
char m_Password[128];
bool Login();
};
私和保护继承方式会更改基类成员在继承类中的权限,导致继承关系复杂,所以实际开发中最常使用的是公继承方式.
C++``构造器的访问顺序
基类和继承类中都有构造器和析构器,则在创建继承类对象时是先构造基类,还是先构造继承类?同样,在释放继承类对象时是先释放基类,还是先释放继承类?
事实上,创建继承类对象时,先调用基类构造器,再调用继承类``构造器;释放继承类对象时则恰好相反,先调用继承类的析构器,再调用基类的析构器.
[实例]定义基类CEmployee和继承类``COperator,观察构造器和析构器的访问顺序,具体代码如下:
cpp
#include<iostream>
using namespace std;
class CEmployee
{
public:
int m_ID;
char m_Name[128];
char m_Depart[128];
CEmployee()
{
cout << "CEmployee类`构造器`被调" << endl;
}
~CEmployee()
{
cout << "CEmployee类`析构器`被调" << endl;
}
};
class COperator : public CEmployee
{
public:
char m_Password[128];
COperator()
{
strcpy(m_Name, "MR");
cout << "COperator类`构造器`被调" << endl;
}
~COperator()
{
cout << "COperator类`析构器`被调" << endl;
}
};
int main(int argc, char* argv[])
{
COperator optr;
return 0;
}
运行结果为:
CEmployee类构造器被调
COperator类构造器被调
COperator类析构器被调
CEmployee类析构器被调
C++``继承类``显式调用``基类构造器
创建继承类对象时,会自动调用基类默认构造器.若想使用基类带参构造器,则要用显式调用方式.
[实例]要求继承类``显式调用基类的构造器.具体代码如下:
cpp
#include<iostream>
using namespace std;
class CEmployee
{
public:
int m_ID;
char m_Name[128];
char m_Depart[128];
CEmployee(char name[])
{
strcpy(m_Name, name);
cout << m_Name << "调用了CEmployee类`带参构造器`" << endl;
}
CEmployee()
{
strcpy(m_Name, "MR");
cout << m_Name << "CEmployee类`无参构造器`被调" << endl;
}
~CEmployee()
{
cout << "CEmployee类`析构器`被调" << endl;
}
};
class COperator : public CEmployee
{
public:
char m_Password[128];
COperator(char name[]) : CEmployee(name)
{
cout << "COperator类`构造器`被调" << endl;
}
COperator() : CEmployee("JACK")
{
cout << "COperator类`构造器`被调" << endl;
}
~COperator()
{
cout << "COperator类`析构器`被调" << endl;
}
};
int main(int argc, char* argv[])
{
COperator optr1;
COperator optr2("LaoZhang");
return 0;
}
运行程序结果为:
JACK调用了CEmployee类带参构造器
COperator类构造器被调
LaoZhang调用了CEmployee类带参构造器
COperator类构造器被调
COperator类析构器被调
CEmployee类析构器被调
COperator类析构器被调
CEmployee类析构器被调
在基类无参构造器中初化成员串数组m_Name为MR.从运行结果上看,继承类``创建对象时没有调用基类无参构造器被调是带参构造器.
C++``继承类屏蔽基类的同名成员函数
如果继承类中定义了一个和基类成员同名的成员函数,则继承类对象调用成员时被调是基类成员函数,还是继承类``成员函数呢?
事实上,基类的同名成员函数(包括重载函数)在继承类中会被屏蔽,因此继承类对象调用是继承类``成员函数.但使用域限定符"::",显式地指定基类名,可调用基类的同名成员函数.
[实例]基类和继承类中都定义了OutputName()``成员函数,观察继承类对象的调用结果.代码如下:
cpp
#include<iostream>
using namespace std;
class CEmployee
{
public:
int m_ID;
char m_Name[128];
char m_Depart[128];
CEmployee() {}
~CEmployee() {}
void OutputName()
{
cout << "调用CEmployee类的OutputName`成员函数`" << endl;
}
};
class COperator : public CEmployee
{
public:
char m_Password[128];
void OutputName()
{
cout << "调用COperator类的OutputName`成员函数`" << endl;
}
};
int main(int argc, char* argv[])
{
COperator optr;
optr.OutputName();
//调用`继承类``COperator`的`OutputName()``成员函数`
return 0;
}
运行程序结果为:
调用COperator类的OutputName``成员函数
可见,代码"optr.OutputName();"调用的是继承类``COperator的OutputName()``成员函数,而不是基类CEmployee的OutputName()``成员函数.
如果用户想访问基类的OutputName()函数,需要在成员函数名前使用域限定符"::",显式地指定基类名.如:
cpp
optr.CEmployee::OutputName();
//调用`CEmployee`类的`OutputName()``成员函数`
注意,假设继承子类后,定义了一个基类指针,继承类的构造器为其创建对象.如:
cpp
CEmployee *pWorker = new COperator();
//定义`CEmployee`类指针被调`继承类`的`构造器`
使用pWorker对象调用OutputName()函数,如执行代码:
cpp
pWorker->OutputName();
则调用是CEmployee类的OutputName()函数.编译器对OutputName()``成员函数是静态绑定,即根据定义对象时的类型,确定调用哪个类的成员函数.
pWorker属于CEmployee类型,因此调用是CEmployee类的OutputName()``成员函数.