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; //failed
return 0;
}
实验结果:
调用CDrived的虚函数
调用CBase的非虚函数
调用CBase的虚函数
调用CBase的非虚函数
调用CNext的虚函数
调用CBase的非虚函数
调用CBase的虚函数
调用CBase的非虚函数
可见,在子类的子类CNext中,CNext类型可以转换为基类型CBase。