抽象基类
纯虚函数:
我们可以将一个函数定义为纯虚函数来告诉这个函数是没有实际意义的。和普通函数不同的是纯虚函数无须定义。通过在函数的声明语句的分号前加上=0来说明一个虚函数时纯虚函数。值得注意的是,可以为纯虚函数添加定义,但是必须是在类的外部,即不能在类的内部为一个=0的函数提供函数体。
含有纯虚函数的类是抽象基类:
抽象基类只负责定义接口,而后续的其他类可以覆盖接口,不能直接创建一个抽象基类的对象。
抽象基类的派生类应该要给出自己对基类中纯虚函数的定义,否则它将仍然是一个抽象基类。
派生类构造函数只初始化它的直接基类。
访问控制与继承
受保护的成员:
一个类可以通过protected关键字来声明那些它希望与派生类分享而不让其他类访问的成员:
1.和私有成员类似,受保护的成员对于类的用户来说是不可访问的;
2.与公有成员类似,受保护的成员对派生类的成员和友元来说是可以访问的;
3.派生类的成员或友元只能通过派生类对象来访问基类的受保护成员。派生类对一个基类对象中的受保护成员没有访问权限,例如:
class Base{
protected:
int prot_mem;
};
class Sneaky:public Base{
friend void clobber(Sneaky&); //能访问Sneaky::prot_mem
friend void clobber(Base&); //不能访问Base::prot_mem
int j; //默认为private
};
//正确,该函数能访问Sneaky的private和protected成员
void clobber(Sneaky& s){
s.j = s.prot_mem = 0;
}
//错误,该函数不能访问Base的protected成员
void clobber(Base& b){
b.prot_mem = 0;
}
公有、私有和受保护继承:
某个类对继承来的成员的访问受到两个因素的限制:一是在基类中该成员的访问说明符;二是派生类的派生列表中的访问说明符。举个例子:
class Base{
public:
void pub_mem();
protected:
int prot_mem;
private:
char pri_mem;
};
class Pub_Derv:public Base {
int f(){ retrun prot_mem; } //正确,
char g() {return pri_mem; } //错误,私有成员不能访问
};
class Pri_Derv:private Base { //私有继承并不影响派生类访问权限
int f1(){ retrun prot_mem; } //正确,
char g1() {return pri_mem; } //错误,私有成员不能访问
};
可以看出不管是私有继承还是公有继承,都不会改变派生类对基类成员的访问权限,它们的作用是将基类中继承而来的成员改变访问权限,比如公有继承下,pub_mem()在派生类中是public的,pro_mem还是protected的,pri_mem还是private的;在保护继承下,pub_mem()在派生类中式protected的,pro_mem还是protected的,pri_mem还是private的;在私有继承下,pub_mem()在派生类中是private的,类的用户将不能调用该函数,pro_mem是private的,pri_mem还是private的:
class Dervied_Pu:public Pub_Derv{
//正确,Base类的prot_mem在Pub_Derv中仍然是protected的
int use_base(){return prot_mem;}
};
class Dervied_Pr:private Pri_Derv{
//错误,Base类的prot_mem在Pri_Derv中已经是private的了
int use_base(){return prot_mem;}
};
派生类向基类转换的可访问性:
派生类向基类的转换是否可访问由使用该转换的代码决定,同时派生类的派生访问说明符也有影响。假设D继承自B:
1.只有当D公有继承自B时,用户代码才能使用派生类向基类的转换,另外两种继承则不能;
2.不论哪种继承方式,D的成员函数和友元都能使用派生类向基类的转换;派生类向其直接基类的转换对于派生类的成员函数和友元来说永远是可访问的;
3.如果D继承B是公有或受保护的,则D的派生类成员和友元可以使用D向B的类型转换,私有继承则不可以。
类的设计与受保护成员:
不考虑继承的话,我们可以认为类有两种用户:普通用户和类的实现者。其中,普通用户编写的代码使用类的对象,这部分代码只能访问类的公有成员;实现者则负责编写类的成员和友元的代码,成员和友元既能访问类的公有成员也能访问类的私有成员。
如果进一步考虑的话,类就会有第三类用户:派生类。基类将它希望被派生类访问的成员声明为受保护的,但是派生类及其友元仍然不能访问基类的私有成员。
友元继承:
就像友元关系不能传递一样,友元关系同样不能继承。基类的友元在访问派生类时没有特殊性,同样派生类的友元也不能随意访问基类的成员;同样友元的基类或派生类在访问声明友元的类时也没有特殊性:
class Base{
friend class Fri;
private:
int b_mem;
};
class Sb:public Base{
private:
int s_mem;
};
class Fri{
public:
int f(Base b){ //正确
return b.b_mem;
}
int f2(Sb s){ //错误,Fri不是Sb的友元
return s.s_mem;
}
int f3(Sb s){ //正确,对基类部分的访问权限由基类自己控制
return s.b_mem;
}
};
class Sf:public Fri{ //友元类的派生类
public:
int mem(Base b){
return b.b_mem; //错误
}
};
改变个别成员的可访问性:
有时需要改变派生类继承的某个名字的访问级别,通过使用“using”声明可达到这一目的:
class Base{
public:
int b_m;
protected:
int b_me;
private:
int b_mem;
};
class Sb2:private Base {
protected:
using Base::b_m;
using Base::b_me;
};
class Sb2:private Base {
private:
using Base::b_m; //不能被下级派生类访问,也不能被外部访问
using Base::b_me; //不能被下级派生类访问,也不能被外部访问
};
class Ssb2:public Sb2 {
public:
int f(){
return b_m;
}
};
int main(void){
Sb s;
Sb2 s2;
s.b_m = 1; //正确
s.b_me = 2; //正确
s2.b_m = 3; //错误
}
默认的继承保护级别:
默认情况下,使用class关键字定义的派生类是私有继承的;而使用struct关键字定义的派生类是公有继承的:
class Base{};
struct D1:Base{}; //默认public继承
class D2:Base{}; //默认private继承