派生类到基类转换的可访问性——新解

来源:互联网 发布:知乎的缺点 编辑:程序博客网 时间:2024/06/05 09:01

C++ primer第四版15.3小节有这样一段话:
如果是 public 继承,则用户代码和后代类都可以使用派生类到基类的转换。

如果类是使用 private 或 protected 继承派生的,则用户代码不能将派生类型对象转换为基类对象

 首先明白:什么是可访问,个人觉得可访问就是可出现。A可以访问B,就说名B可以直接在A中出现。

派生类到基类的转换包括:派生类对象的引用转换为基类对象的引用;派生类对象的指针转换为基类对象的指针;派生类对象转换为基类对象。

//Base.h#ifndef _BASE_H_#define _BASE_H_#include <IOSTREAM>using namespace std;class CBase{public:CBase();
         virtual ~CBase(){};CBase(const CBase & b);virtual void vir_display() const;void Novir_display()const;private:int val;};#endif //_BASE_H_


 

//Base.cpp#include "Base.h"void CBase::Novir_display() const{cout << "调用CBase的非虚函数" << endl;}void CBase::vir_display() const{cout << "调用CBase的虚函数" << endl;}CBase::CBase(){val =0;}CBase::CBase(const CBase & b ){val = b.val;}<pre class="cpp" name="code">//Derived.h#ifndef _DERIVED_H_#define _DERIVED_H_#include "Base.h"class CDrived : public CBase{public:CDrived();CDrived(const CDrived & d);public:void Novir_display() const;void vir_display() const;private:double data1;double data2;};#endif //_DERIVED_H_
//Derived.cpp#include "Derived.h"void CDrived::Novir_display() const{cout << "调用CDrived的非虚函数" << endl;}void CDrived::vir_display() const{cout << "调用CDrived的虚函数" << endl;}CDrived::CDrived(){}CDrived::CDrived( const CDrived & d ){data1 = d.data1;data2 = d.data2;}
//show_main.cpp#include "Base.h"#include "Derived.h"void show_Obj(const CBase b){b.Novir_display();b.vir_display();}void show_Ptr(const CBase& b){b.Novir_display();b.vir_display();}int main(){CDrived d;show_Obj(d);show_Ptr(d);return 0;}
实验结果:
调用CBase的非虚函数调用CBase的虚函数调用CBase的非虚函数调用CDrived的虚函数请按任意键继续. . .
解析:
CDrived 继承CBase的方式为public,故上面可以执行。如果,继承方式换成protected,则出现error:“类型转换”: 从“CDrived *”到“const CBase &”的转换存在,但无法访问。因为在show_main.cpp中,CDrived对象转换为CBase对象时需要调用CBase的复制构造函数,而此时CBase的构造函数都变成了protected,在用户代码中无法访问。
对于show_Ptr(d)也出现这个错误,但不是由于调用CBase的复制构造函数产生的,如果声明为void show_Prt(CBase&),则错误为:从“CDrived *”到“CBase &”的转换存在,但无法访问。可见error中的const也跟着消失了。为何会出现这个错误呢?目前没搞懂,求高人赐教。
个人理解:对于private或protected继承,派生类对象中的基类对象部分已经变成private或protected的,故无法在用户代码中访问。
 

再在Derived.h中添加三个函数来制造派生类对象到基类对象的转换,此时CDrived是protected继承方式

void CDrived::dis_Ref(const CBase& b ){b.vir_display();b.Novir_display();}void CDrived::dis_Obj(const CBase b ){b.vir_display();b.Novir_display();}void CDrived::test_CDrived(){//在子类中访问子类转换为基类类型CDrived d;dis_Ref(d);dis_Obj(d);}


//show_main.cpp

int main(){CDrived d;         d.test_CDrived();return 0;}

调用CDrived的虚函数
调用CBase的非虚函数
调用CBase的虚函数
调用CBase的非虚函数

可见,protected继承时转换是在派生类中可访问的,即protected继承时,在派生类中派生类到基类的转换是可访问的,在用户代码是不可访问的。

二、如果是 private 继承,则从 private 继承类派生的类不能转换为基类。

先看部分三,然后修改为class CDrived :protected CBase即可,其他不变。结果出现上面的类型转换错误。

三、如果是 protected 继承,则后续派生类的可以访问后续派生类转换为基类类型。

定义class CNext : public CDrived,此时class CDrived :protected CBase。

//Next.h

#ifndef _NEXT_H_#define _NEXT_H_#include "Derived.h"class CNext : public CDrived{public:  void Novir_display();void dis_Ref(const CBase& b);void dis_Obj(const CBase b);void test_CNext();virtual void vir_display() const;};#endif //



//Next.cpp

#include "Next.h"void CNext::Novir_display(){cout << "调用子类的子类的非虚函数" <<endl;}void CNext::dis_Ref(const CBase& b ){b.vir_display();b.Novir_display();}void CNext::dis_Obj( const CBase b ){b.vir_display();b.Novir_display();}void CNext::test_CNext(){//在子类的子类中访问子类的子类转换为基类类型CNext cn;dis_Ref(cn);dis_Obj(cn);}void CNext::vir_display() const{cout << "调用CNext的虚函数" << endl;}


 

//show_main.cpp

#include "Base.h"#include "Derived.h"#include "Next.h"int main(){CDrived d;           d.test_CDrived();CNext cn;cn.test_CNext();// CBase *pb = new CNext; //failed//CDrived * pd = new CNext; //failedreturn 0;}


实验结果:

调用CDrived的虚函数
调用CBase的非虚函数
调用CBase的虚函数
调用CBase的非虚函数
调用CNext的虚函数
调用CBase的非虚函数
调用CBase的虚函数
调用CBase的非虚函数

可见,在子类的子类CNext中,CNext类型可以转换为基类型CBase。

0 0