题外话,关于基类的protected成员(虽然是题外话,但是这个点对于讲继承很重要)
在总结这三种继承方式之前,要先澄清两个误区。
一、类的访问权限修饰,是类级别的,也就是class level的,而不是对象级别的。同一个类的两个不同的对象,是可以互相访问对方的保护成员和私有成员的。
二、好多人对下面这句话都曾经有过一个错误的认识。
“对于基类的protected成员,派生类的成员函数和友元函数具有访问权限。”(这是一句不应该说的话,因为表达出来的意思很容易让人产生误解)
这句话说的比较模糊,完整清楚的表达应该是:“派生类的成员函数和友元函数,可以访问派生类对象中的基类部分的protected成员(和public成员)。但是,对于独立的基类对象中的protected成员,派生类是没有访问权限的,除非又把派生类声明为基类的友元类。”
为了理解上面这一段话,请考虑下面这个例子。
class Base{
public:
void pub_mem();
protected:
int prot_mem;
private:
char priv_mem;
};
class Derived : public Base{
public:
int visitBaseObj(Base&);
int visitBaseObj_1();
}
int Derived::visitBaseObj(Base& Bobj){
return ( Bobj.prot_mem ); //错误,不能通过基类对象直接访问Base的protected成员
}
int Derived::visitBaseObj_1(){
return prot_mem; //正确,派生类的成员函数和友元函数,可以访问派生类的基类部分的protected成员
}
题外话说完,回到正题。
C++的继承有三种方式,public继承、protected继承和private继承。
提到继承,就肯定会提到基类和派生类,注意,我们这里说的基类都是默认指的直接基类,如果用到间接基类,会显式强调指出的。
- 这三种继承不论是哪种继承,基类的private在派生类的基类部分,还是基类部分的private,这个private对子类都是拒绝访问的。
- 对于public继承:就是把基类的public变成了派生类中带有基类域属性的public,把基类的protected变成了派生类中带有基类域属性的protected;
- 对于protected继承:就是把基类的public和protected变成了派生类中带有基类域属性的protected,注意这个protect的所有权是属于派生类的;
- 对于private继承:就是把基类的public和protected变成了派生类中带有基类域属性的private,注意这个private的所有权是属于派生类的。
- 类的对象只能访问本类中的public成员,类中的成员函数和友元可以访问类中(除了基类部分的private成员)的所有成员。
下面展开来细讲一下。
总的来说,某个类对其继承而来的成员的访问权限受到两个因素的影响:一是在基类中该成员的访问说明符;二是在派生类的派生列表中的访问说明符。
值得注意的是,对于派生类中的成员函数和友元函数能否访问 派生类的基类部分的成员,派生列表中的访问说明符 是不会产生什么影响的。
派生类中的成员函数和友元函数,对派生类的基类部分的成员的访问权限,只与基类中的访问说明符有关。举个例子:
class Base{
public:
void pub_mem();
protected:
int prot_mem;
private:
char priv_mem;
};
struct Pub_Derv : public Base{
//f()函数正确,派生类可以访问 派生类的基类部分 的protected成员
int f() { return prot_mem; }
//g()函数错误,派生类不可以访问 派生类的基类部分 的private成员
char g() { return priv_mem; }
};
//对于派生类的成员函数和友元函数 的访问权限,private和protected继承对其不产生任何影响(但是一会儿要讲,会影响派生类对象的访问权限)
struct Priv_Derv : private Base{
//f()函数正确,派生类可以访问 派生类的基类部分 的protected成员
int f() { return prot_mem; }
//g()函数错误,派生类不可以访问 派生类的基类部分 的private成员
char g() { return priv_mem; }
};
看到这里,我们知道了 派生列表中的访问说明符,对于 派生类中的成员函数和友元函数能否访问 派生类的基类部分的成员,不会产生任何影响。
那么派生列表中的访问说明符,有什么作用呢?有两点作用。
- 第一点作用:派生访问说明符,是用于控制派生类用户(包括派生类的派生类在内),对于派生类的基类部分的成员的访问权限。 派生类用户,说白了就是指由派生类生成的对象。类的对象只能调用本类中的public成员。
接着上面的例子,
Pub_Derv pub_obj;
Priv_Derv priv_obj;
pub_obj.pub_mem(); //正确,派生类对象可以访问派生类的基类部分的public成员
pub_obj.prot_mem; //错误,对象只可以访问本类的public成员
priv_obj.pub_mem(); //错误,派生类对象不可以访问派生类的基类部分的原先的public成员,因为此时pub_mem()在派生类中是private的
- 第二点作用:派生类访问说明符还可以控制继承自派生类的新类的访问权限。
如果是protected继承,则在Prot_Derv类中,继承自Base的所有public和protected成员都是protected的,而Base中原有的private成员仍是派生类的基类部分自己private的。
所以可以看到:
- private继承截断了继承的访问通道,下一个孙子派生类将无法访问爷爷基类的public和protected成员,同时关闭了派生类对象直接访问基类public成员的通道。
- protected继承则依旧保持了继承访问通道的畅通,但同时也关闭了派生类对象直接访问基类public成员的通道。
私有继承和保护继承,建立的是has-a关系。所以这两种继承又被称为继承实现,但不继承接口。因为派生类对象不能显式地使用基类的接口。因此,不能将派生类对象看做是一种基类对象。也正是 由于这个原因,在不进行显式转换的情况下,基类指针或引用将不能指向派生类对象。