C++中关于私有继承向上转型的问题

对于public而言,将一个继承类对象转换为一个基类对象再正常不过,但是,对于private来说,编译器不允许隐式的转换,强制转换也有些许的约束。

也就是说下面这样的代码并不可以

#include 

using namespace std;

class A{
    private:
    virtual void foo()
    {
        cout << "A::foo" << endl;
    }  
};
class B : private A{
    public:
    void foo() override {
        cout << "B::foo" << endl;
    }
};
int main()
{
    As* pa = static_cast (new B()); //error
    B b;
    b.foo();
    return 0;
}		

为了理解为什么这样,我们可以首先应当理解转换的原理。下面我们将隐式的强制转换或者显示的强制转换称为转换。

转换并不是一个replace的,而是非replace,也就是说,我们在进行转换地时候,编译器会为我们生成一个临时地右值。

比如说下面地代码

double a = 1.31;
(int) a;
int b = a;

熟悉C的读者可能很容会想起来这个机制。

对于自定义类型来说,也同样地遵循这个机制。

好的,现在我们再看看为什么不能将私有继承的对象进行向上的转换。为了进行向上的转换,我们需要调用基类的构造函数来构造一个基类。但同时,私有继承也就意味着对于类外构造函数是不可见的,这里的不可见不是说不知道,而是说不可访问的意思。那这样怎么能构造一个基类的对象呢?

或许我们可以放大以下视野,如果说基类中的构造函数不能对外公开,那么有由子类转换为基类就是一个不可行的行为。这让你想到了什么?没错,就是在C++11之前,我们禁止某个对象的赋值操作,可以将其的所有copying行为的函数(编译器为我们生成的)声明为private,并不予任何实现。又或者是boost库中的nocopyable,他的实现这里就不说了,读者可以很轻松的找到。

让我们将视野放的更广阔一些,让我们从不同权限继承的意义来说。

公有继承意味着我们实现了一个is-a的设计——如果对于公有继承你了继承体系之间不是这样的关系,哦,那赶快重构代码吧。你选错了设计模式。

一旦组织了一个is-a的关系,那也就意味着对于基类的所有行为,也都使用于继承类。所以,这样的向上转换在某些场合下完全没有问题。你并没有理由拒绝这样做。

再让我们看看私有继承意味着什么吧,私有继承意味着根据某物实现出。

这也就意味着,私有继承纯粹只是一种实现技术 (基类所有的所有数据在子类中都是private的),更加具体的来说,私有继承继承了实现方法,而没有继承接口。

好的,既然这样,我们编译器如果为我们做出一个隐式的转换,这样的转换并不是时时刻刻都尽如人意的,所以说编译器拒绝为我们生成这样的代码。你有可能说这样是强词夺理,本来就是因为构造函数的私有性质才不能进行转换的。但你可别忘了,为我们进行隐式转换的工作可是编译器啊,如果读者有兴趣的话,可以看看std::initlized_list是如何实现的——其中就有一个私有的构造函数,让编译器来调用。所以,我的意思是,编译器完全有能力去进行隐式转换,但是那样既没有没有什么必要,又不符合C++语法规则的直觉。

说一些题外话:private继承用的真的是很少很少,但还是有其存在的必要。像Java那样的语言——没有私有继承,虽然也可以做任何你在C++中想做的,但是失去了一些实现方法的弹性。我们喜欢C++不正是喜欢C++的语言的弹性吗?

到了这里,对应当解决的问题和对于问题的一些拓展大致的说了一下,如果想要真正的理解private继承的前世今生,或者说是继承体系的前世今生,应当学习更多的东西。

你可能感兴趣的:(C++,c++,开发语言,继承,类型转换)