Хобрук: Ваш путь к мастерству в программировании

Почему производный класс не может вызвать защищенную функцию-член в этом коде?

#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;
};
28.05.2013

  • protected похож на частный, и он также может использоваться как частный метод другими производными классами, но вы пытаетесь использовать его как общедоступный метод 28.05.2013

Ответы:


1

Несмотря на то, что управление доступом в C++ работает для каждого класса (а не для каждого экземпляра), спецификатор доступа protected имеет некоторые особенности.

Спецификация языка хочет гарантировать, что вы получаете доступ к защищенному члену некоторого базового подобъекта, который принадлежит производному классу. Вы не должны иметь доступ к защищенным членам некоторых несвязанных независимых объектов базового типа. В частности, вы не можете получить доступ к защищенным членам автономных объектов базового типа. Вам разрешен доступ только к защищенным членам базовых объектов, которые встроены в производные объекты в качестве базовых подобъектов.

По этой причине для доступа к защищенным членам необходимо использовать синтаксис pointer->member, reference.member или object.member, где указатель/ссылка/объект ссылается на производный класс.

Это означает, что в вашем примере защищенный элемент somethingProtected() недоступен через Base объектов, Base * указателей или Base & ссылок, но доступен через Derived объектов, Derived * указателей и Derived & ссылок. Ваш обычный доступ somethingProtected() разрешен, так как это просто сокращение для this->somethingProtected(), где this имеет тип Derived *.

b.somethingProtected() нарушает вышеуказанные требования.

Обратите внимание, что в соответствии с приведенными выше правилами в

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

первый вызов также завершится ошибкой, а второй будет скомпилирован, даже если оба пытаются получить доступ к одному и тому же объекту.

28.05.2013
  • есть ли соответствующая цитата из стандарта, которая описывает это? 14.08.2015
  • @YoungJohn: 11.4 Защищенный доступ* говорит, что когда ссылка на защищенный член происходит у друга или члена некоторого класса C и происходит через a (возможно, неявно ) объектное выражение, то класс объектного выражения должен быть C или классом, производным от C. В приведенном выше примере доступ происходит от члена Derived. При первом доступе выражение объекта равно *b, а при втором доступе - *this. В соответствии с приведенным выше правилом, первый доступ недействителен, а второй доступ в порядке. 14.08.2015
  • Как насчет EDIT2 о частном доступе? Насколько я понимаю, this-›somethingProtected является частным случаем someObject-›somethingProtected, скажем, это указатель на класс C, до тех пор, пока this-›somethingProtected работает, someObject-›somethingProtected также должен работать, только если класс someObject указывает на то же, что и C, или класс, производный от C. Правильно ли это? 06.02.2016
  • @AnT Я не могу придумать ни одного случая для класса, производного от C, не могли бы вы привести пример? 06.02.2016
  • Я понимаю правило. Однако я не понимаю, почему он существует. Что плохого в вызове защищенных членов какого-либо другого объекта базового типа? При использовании виртуальных функций это может быть полезно. 08.09.2016
  • @Silicomancer, как указано в этом ответе, а также в одном здесь, это механизм, обеспечивающий интерфейс несвязанных объектов (например, других типов, производных от той же базы) не нарушаются. Как это может быть полезно в виртуальных функциях? 29.12.2017
  • Обратите внимание, что вы можете вызывать static защищенных членов базового класса. 27.02.2020

  • 2

    Я полагаю, у вас есть некоторая путаница в том, как получить доступ к членам базового класса. Только так:

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

    в вашем примере вы пытаетесь получить доступ к защищенному члену другого экземпляра.

    производный экземпляр будет иметь доступ к своим собственным защищенным членам, но не к другим защищенным членам экземпляра класса, это предусмотрено дизайном.

    На самом деле доступ к защищенным членам другого класса, из других членов экземпляра или из основной функции фактически находится под публичным доступом...

    http://www.cplusplus.com/doc/tutorial/inheritance/ (см. для таблицы спецификаторов доступа, чтобы увидеть разные уровни)

    Оба примера доказывают одно и то же, например:

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

    здесь ваш производный класс получает b в качестве параметра, поэтому он получает еще один экземпляр базы, а затем, поскольку b.somethingProtected не является общедоступным, он не будет соответствовать.

    это будет соответствовать:

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

    ваш второй пример соответствует требованиям, потому что вы обращаетесь к общедоступному методу в другом классе d

    >  void somethingDerived(Base& b)
    >     {
    >         b.somethingProtected();  // This does not
    >     }
    
    28.05.2013
  • но я могу это сделать: 'void somethingDerived(Derived& d)' компилируется нормально 28.05.2013
  • вы обращаетесь к общедоступному методу, это нормально. вы также можете вызвать что-то из основного... см. мое редактирование. 28.05.2013
  • Нет, они обращаются к защищенному методу базового класса. 28.05.2013
  • @juanchopanza нет, они обращаются к защищенному методу метода базового класса другого экземпляра... этого не может быть... 28.05.2013
  • Ну, они утверждают, что это действительно происходит. 28.05.2013

  • 3

    Класс Derived может получить доступ к защищенному базовому элементу только в объектах Derived. Он не может получить доступ к члену в объектах, которые (обязательно) не являются Derived объектами. В тех случаях, когда это не удается, вы пытаетесь получить доступ к элементу через Base &, и, поскольку это может относиться к объекту, отличному от Derived, доступ не может быть осуществлен.

    28.05.2013

    4

    То, что вы сделали, является незаконным в 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.
        }
    };
    
    28.05.2013
  • Это не объясняет void somethingDerived(Derived& d) 28.05.2013
  • Новые материалы

    Управление DOM для чайников вроде меня
    Одной из первых вещей, которую мы рассмотрели, когда начали изучать Javascript во Flatiron, была модель DOM. Кто он? Чем он занимается? Он больше машина, чем человек? Ну да довольно много. ДОМ..

    Что такое структура данных?
    Структура данных хранит и извлекает данные. Все, что обеспечивает эти две функции, является структурой данных . Период. Вы можете пропустить оставшуюся часть статьи, если ответ..

    мои январские чтения по программированию
    Эрик Эллиот Программирование приложения JavaScript Эл Свейгарт «Автоматизируйте скучные вещи с помощью Python» Прогрессивное веб-приложение Google..

    Создание ассоциаций секвелизации с помощью инструмента командной строки Sequelize
    Sequelize - популярный, простой в использовании инструмент объектно-реляционного сопоставления (ORM) JavaScript, который работает с базами данных SQL. Довольно просто начать новый проект с..

    Искусственный интеллект в юридической отрасли - пример прогнозирования судебных решений с помощью глубокого обучения
    На протяжении всей истории люди полагались на суды, присяжных, королей и королев в отправлении правосудия. Сегодня способность судов обеспечивать справедливое и быстрое правосудие для своих..

    Введение в машинное обучение для обнаружения аномалий (часть 1)
    Тщательно созданный, тщательно спроектированный ресурс для специалистов по данным. Часть 1 Главы 03 из Руководства по машинному обучению для обнаружения аномалий Внимание! Прежде чем вы..

    Начало работы с Pulumi в Digital Ocean
    Цифровой океан (ДО) — отличная альтернатива многим другим поставщикам облачных услуг. DO предоставляет простой и понятный пользовательский интерфейс, упрощающий управление инфраструктурой и..