为什么派生类不能在此代码中调用受保护的成员函数?


67
#include <iostream>

class Base
{  
protected:
    void somethingProtected()
    {
        std::cout << "lala" << std::endl;
    }
};

class Derived : public Base
{
public:
    void somethingDerived()
    {
        Base b;
        b.somethingProtected();    // This does not compile
        somethingProtected();      // But this is fine
    }
};

int main()
{
    Derived d;
    d.somethingDerived();
    return 0;
}

我认为也许只有的受保护成员this可以使用,而其他实例的受保护成员永远无法访问。

但:

class Derived : public Base
{
public:

    void somethingDerived(Derived& d)
    {
        d.somethingProtected();  // This compiles even though d is
                                 // potentially a different instance
    }

    void somethingDerived(Base& b)
    {
        b.somethingProtected();  // This does not
    }
};

因为我已经用C ++编程一段时间了,所以我对此感到恶心,但是我找不到这种行为的任何解释。

编辑:

相同或不同的实例无关紧要:

int main()
{
    Derived d1, d2;          // Two different instances
    d1.somethingDerived(d2); // This compiles fine
    d1.somethingDerived(d1); // This compiles fine
    return 0;
}

编辑2:

看来,在访问权限方面,使用什么类的实例根本不重要:

class Base
{
public:
    void something(Base& b)  // Another instance
    {
        ++b.a;               // But can enter private members
    }

private:
    int a;
};

3
protected就像private一样,其他派生类也可以像private方法一样使用它,但是您尝试像public方法一样使用它
fatihk

Answers:


82

即使C ++中的访问控制按类(而不是按实例)工作,protected访问说明符仍具有一些特殊性。

语言规范希望确保您正在访问属于派生类的某些基础子对象的受保护成员。您不应该能够访问基本类型的某些不相关独立对象的受保护成员。特别是,您不能访问基本类型的独立对象的受保护成员。仅允许您访问嵌入的基础对象的受保护成员到派生对象中作为基础子对象。

出于这个原因,你必须通过访问受保护成员pointer->member的语法,reference.memberobject.member语法,在指针/参考/对象指的是派生类。

这意味着在您的示例中,受保护的成员somethingProtected()不能通过Base对象,Base *指针或Base &引用进行访问,但是可以通过Derived对象,Derived *指针和Derived &引用进行访问。您的普通somethingProtected()访问权限是允许的,因为这只是this->somethingProtected()wherethis类型的简写Derived *

b.somethingProtected() 违反上述要求。

请注意,按照上述规则

void Derived::somethingDerived()
{
    Base *b = this;
    b->somethingProtected();    // ERROR
    this->somethingProtected(); // OK
}

即使两者都试图访问同一实体,第一个调用也将失败,而第二个将编译。


1
标准中有相关的报价来描述吗?
YoungJohn

5
@YoungJohn:11.4受保护的访问*表示,当对受保护成员的引用“发生在某个C类的朋友或成员中”并通过“(可能是隐式)对象表达式”出现时,则“该对象表达式的类应是C或从C派生的类。在上述示例中,访问来自的成员Derived。在第一个*b访问中,对象表示是,而在第二个访问中,对象表示是*this。根据上述规则,第一次访问无效,第二次访问正常。
AnT

关于私人访问的EDIT2呢?我的理解是this-> somethingProtected是someObject-> somethingProtected的特例,假设这是指向C类的指针,只要this-> somethingProtected起作用,someObject-> somethingProtected也应该仅在someObject类下起作用指向C还是C派生的类。这对吗?
杰里

2
我了解规则。但是我不明白为什么它存在。调用基本类型的其他某个对象的受保护成员有何危害?使用虚函数时,这可能会很有用。
Silicomancer

1
@Silicomancer,如本答案以及此处所述,这是一种机制,可确保不侵犯无关对象(例如,从同一基数派生的其他类型)的接口。这在虚函数中如何有用?
克里斯·亨特

3

我相信您对如何访问基类成员有些困惑。只有这样:

class Derived : public Base
void drivedMethod() {
    Base::baseMethod();
}

在您的示例中,您尝试访问另一个实例的受保护成员。

一个派生实例将有权访问其自己的受保护成员,但不能访问另一个类实例受保护的成员,这是设计使然。

实际上,从另一个实例成员或从main函数访问另一个类的受保护成员实际上都处于公共访问权限下。

http://www.cplusplus.com/doc/tutorial/inheritance/ (查找访问说明符表以查看不同的级别)

这两个例子证明了同一件事,例如:

void somethingDerived(Base& b)
    {
        b.somethingProtected();  // This does not

在这里,您的Derived类将b作为参数,因此它正在获取base的另一个实例,然后由于b.somethingProtected不是公共的,因此将不符合要求。

这将包括:

void somethingDerived()
{
   Base::somethingDerived();

您的第二个示例合规性很好,因为您正在访问另一个d类上的公共方法

>  void somethingDerived(Base& b)
>     {
>         b.somethingProtected();  // This does not
>     }

1
但是我可以做到:'void somethingDerived(Derived&d)'编译正常
Martin Drozdik

1
您正在访问公共方法,那很好。您也可以从main调用某事物。
Dory Zidon

1
不,他们访问基类的受保护方法。
juanchopanza

@juanchopanza不,他们正在访问另一个实例的基类method..can't发生的一个受保护的方法..
多莉西顿

好吧,他们声称这确实发生了。
juanchopanza

2

Derived类只能访问受保护的基本部件Derived的对象。它不能访问不是(不一定是)Derived对象的对象中的成员。在失败的情况下,您尝试通过来访问成员Base &,并且由于它可能引用的对象不是Derived,所以无法进行访问。


1

您所做的事情在C ++中是非法的。类的对象不能访问受保护的成员。只有成员函数才能访问受保护的成员。protected成员的行为就像私有成员一样,只是被派生类继承。考虑下面给出的程序,以了解私人,公共和受保护成员之间的区别。

class Base
{
    private:
    void somethingPrivate()
    {
        std::cout << "sasa" << std::endl;
    }
    public:
    void somethingPublic()
    {
        std::cout << "haha" << std::endl;
    }
    protected:
    void somethingProtected()
    {
        std::cout << "lala" << std::endl;
    }
};

class Derived : public Base
{
public:
    void somethingDerived()
    {
       Base b;
       b.somethingPublic();   // Works fine.
       somethingProtected();  // This is also fine because accessed by member function.
       //b.somethingProtected();  // Error. Called using object b.
       //somethingPrivate();      // Error. The function is not inherited by Derived.
    }
};

1
这不能解释void somethingDerived(Derived& d)
juanchopanza
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.