在base / derived类的成员函数/ friend函数中导出*到Base *转换

时间:2015-02-28 17:32:07

标签: c++ class inheritance derived-class

以下是代码:

class B;
class C;
class D;
class A{
    public:
        virtual ~A(){}
        friend void gg(D* d);
        void ga(B *b,C* c,D* d);
    };

class B:protected A{
    public:
        void gb(B *b,C* c,D* d);
};
class C:public B{};
class D:public C{};

void A::ga(B *b,C*c,D*d){
    A *a1=b;  // error: 'A' is an inaccessible base of 'B'
    A *a2=c;  // error: 'A' is an inaccessible base of 'C'
    A *a3=d;  // error: 'A' is an inaccessible base of 'D'
}
void B::gb(B *b,C*c,D*d){
    A *a1=b;  // no problem here 
    A *a2=c;  //why do classes derived from B can sucessfully convert to A here?
    A *a3=d;  //why do classes derived from B can sucessfully convert to A here?
}
void gg(D* d){
    A* a=d;
}
int main(){
    B b;
    C c;
    D d;
    A a;
    gg(&d);  // error: 'A' is an inaccessible base of 'D'
    a.ga(&b,&c,&d);
    b.gb(&b,&c,&d);
    A a1(d); //error here;Does it mean the implicit conversion in the user code is also user code?
    A a4=d;  //same as above
    return 0;
}

以下是关于派生到基础转换的可访问性的C ++入门说明:

  

•仅当D公开继承时,用户代码才可以使用派生到基础的转换   如果D继承自B,则用户代码可能不使用转换   保护或私人。

     

•D的会员功能和朋友无论如何都可以使用转换为B.   D如何从B继承。直接基类的派生到基础转换是   始终可以访问派生类的成员和朋友。

     

•来自D的成员函数和类的朋友可以使用派生到 -   如果D使用public或protected从B继承,则进行基本转换。这样   如果D从B私下继承,则代码可能不会使用转换。

但是它并没有谈到B从C / D到A的转换或从A / B / C / D到A的转换发生在第一次转换会失败,第二次会成功。但结果(见代码中的评论让我感到惊讶。

A类是B / C / D类的基类,所以我认为A中发生的Derive-to-Base转换(在void A :: ga(B *,C *,D *)中)会成功但是编译器抱怨。从C ++引物的参考规则2我知道在B:gb(B *,C *,D *)中从B到A的转换将成功,但为什么在C / D转换为A无效B :: gb(B *,C *,D *)也成功了吗? 为什么?

2 个答案:

答案 0 :(得分:1)

这是可访问性的乐趣:

  

但它并没有谈到B从C / D到A

的转换

继承是protected:这意味着B知道继承并且可以引用它的基类,而从B派生的类也具有此访问权限。

B会继承A作为private,图片会略有不同:在B中,所有转换仍然会成功(因为B可以访问私有A基础),但是在派生类的函数中所有这些转换都会失败,因为他们无法访问B的私人生活(和继承)。

理由是标准:

  • (11.2 / 5)如果可以访问基类,可以隐式地将指向派生类的指针转换为指向该基类的指针
  • (11.2 / 1)如果使用受保护的访问说明符将类声明为另一个类的基类,则基类的公共成员和受保护成员可作为派生类的受保护成员进行访问。如果使用私有访问说明符将类声明为另一个类的基类,则基类的公共成员和受保护成员可作为派生类的私有成员访问
  • (11.2 / 4) N的基类B可在R处访问,如果(...)R出现在N类的成员或朋友中,并且B的发明公共成员将是私有的或受保护的N成员
  • (5.10 / 3)解释说,如果无法访问基础,则派生到基础的转换是非法的。
  转换发生在A从B / C / D到A ......我认为......成功。

A不是B的后代。因此它无权访问其受保护的私有成员/继承。

答案 1 :(得分:0)

以下是 The C ++ Programming Language

中有关访问说明符的说法
  

基类的访问规范控制对基类成员的访问以及从派生类类型到基类类型的指针和引用的转换。考虑从基类B派生的D类:

     

•如果B是私人基地,其公共和受保护成员只能由成员函数和D的朋友使用。 朋友和D的成员可以转换a D *到B *。

     

•如果B是受保护的基础,则其公共成员和受保护成员只能由成员函数和D的朋友以及成员函数和派生自D的类的朋友使用。 D和朋友的朋友和成员以及从D派生的类成员可以将D *转换为B *。

     

•如果B是公共基础,其公共成员可以被任何功能使用。此外,受保护的成员可以被D的成员和朋友以及来自D的成员和朋友使用。任何函数都可以将D *转换为B *。

当Derive to Base转换发生时,发生转换的代码必须具有从派生类到基类通过继承层次结构的所有可访问性。

鉴于OP中的例子:

... //other same as before
void A::ga(B *b,C*c,D*d){
    A *a1=b;  // error: 'A' is an inaccessible base of 'B'
    A *a2=c;  // error: 'A' is an inaccessible base of 'C'
    A *a3=d;  // error: 'A' is an inaccessible base of 'D'  <<-- I'll explain this conversion 
}
...//other same as before

转化D->A发生在A中,因此A必须具有转化D->C->B->A的可访问性,我们看到D->CC->B是公开的,但是B->A受保护,这意味着D可以转换为B但是将停留在A中的B->A。要在A中启用转换,我们必须在类B或C或D中通过 protected rule.Then编译器不会再抱怨了。

现在如果从B派生的B类和从B派生的C类都继承为私有?

... // others same as before
class B:private A{ // inherited as private
    public:
        void gb(B *b,C* c,D* d);
};
class C:private B{};  // inherited as private 
class D:public C{};

void A::ga(B *b,C*c,D*d){
    A *a1=b; 
    A *a2=c;  
    A *a3=d;  //    <--------------------------- I'll explain this 
}
void B::gb(B *b,C*c,D*d){
    A *a1=b; 
    A *a2=c; 
    A *a3=d;  //   <============================ and this
}
...  // others same as before

在函数A::ga中,转换D->C有效,但在C->BB->A失败,因此我们必须在A类和C类中将A类声明为朋友才能获得可访问性。

在函数B::gb中,转换D->CB->A(通过私有规则)工作,但C->B失败,因此我们必须将类B声明为C类中的朋友在B中实现D->A转换。