Разница между частным, публичным и защищенным наследованием

В чем разница между наследованием public, private и protected в C ++?

Все вопросы, которые я нашел по SO, относятся к конкретным случаям.


person Community    schedule 13.05.2009    source источник


Ответы (16)


Чтобы ответить на этот вопрос, я хотел бы сначала описать аксессоров участников своими словами. Если вам это уже известно, перейдите к заголовку «следующий:».

Мне известны три средства доступа: public, protected и private.

Позволять:

class Base {
    public:
        int publicMember;
    protected:
        int protectedMember;
    private:
        int privateMember;
};
  • Все, что известно о Base, также знают, что Base содержит publicMember.
  • Только дети (и их дети) знают, что Base содержит protectedMember.
  • Никто, кроме Base, не знает privateMember.

Под «осведомлен о» я имею в виду «признать существование и, таким образом, иметь доступ».

следующий:

То же самое происходит с публичным, частным и защищенным наследованием. Давайте рассмотрим класс Base и класс Child, который наследуется от Base.

  • Если наследование public, все, что известно о Base и Child, также знает, что Child наследуется от Base.
  • Если наследование protected, только Child и его дочерние элементы знают, что они наследуют от Base.
  • Если наследование private, никто, кроме Child, не знает о наследовании.
person Anzurio    schedule 13.05.2009
comment
Я хотел бы добавить несколько слов о том, что видимость в C ++ основана на классе, а не на объекте, что означает, что объекты одного класса могут получать доступ к закрытым полям друг друга без ограничений. - person Zhe Chen; 27.04.2015
comment
Если вам сложно это понять, прочтите ответ Кирилла Лядвинского, а затем вернитесь и прочтите это. - person The Vivandiere; 26.06.2015
comment
Это просто еще один случай, который иллюстрирует, как по большей части наследование от SomeBase похоже на жестко запрограммированный способ создания анонимного члена типа SomeBase. Этот, как и любой другой член, имеет спецификатор доступа, который оказывает такое же управление внешним доступом. - person underscore_d; 27.02.2016
comment
@ZheChen, если у меня есть объекты Tom и Jerry класса Person с закрытым полем age, как вы можете получить доступ (и изменить?) Возраст Джерри с помощью Tom? - person gen; 12.07.2016
comment
В последней строке, вы имеете в виду, что никто, кроме Base, не знает о наследовании? - person JobHunter69; 06.10.2016
comment
@ZheChen: Часть после «что означает, что» верна. Предыдущая часть верна только для private. protected объявления базового класса доступны для дочернего объекта, а не для class: struct Base { protected: int x; }; struct Desc : Base { Desc(Base& b) { (void)b.x; } }; не компилируется. - person lorro; 08.04.2017
comment
Я хочу добавить одно фундаментальное отличие: в Java все абстрактные методы должны быть объявлены общедоступными в производном классе / подклассе. Но в частном наследовании C ++ методы можно объявить частными. Предположим, что класс Controller: private ActionListener {private: int onAction () {...} void setup () {registerActionListener (* this);}} Здесь (* this) передается как объект ActionListener, даже если они объявлены закрытыми. - person shuva; 26.05.2017
comment
Java в этом смысле тупая - person Assimilater; 06.07.2017
comment
Не могли бы вы проиллюстрировать, что вы имеете в виду, говоря о «наследовании»? Я понимаю, что могу получить к нему доступ. Я не могу получить к нему доступ, но я не понимаю, когда кто-то говорит, что я знаю, что A наследуется от B. Что я здесь делаю, я проверяю наследование? - person lineil; 11.09.2018
comment
@underscore_d анонимный член типа SomeBase он не анонимен: он называется Derived::SomeBase - person curiousguy; 29.11.2018
comment
@ZheChen, если у меня есть два объекта: b1 и b2 одного и того же класса Base, тогда как b1 получить доступ (а также изменить) частные члены b2? - person Milan; 21.08.2020
comment
@ Милан Это возможно. Это можно доказать с помощью простого фрагмента: repl.it/repls/CarefulPinkSpof#main.cpp - person Zhe Chen; 22.08.2020
comment
@ZheChen большое спасибо за то, что поделились фрагментом кода. Я никогда не думал об этом. Как теперь я понял вашу точку зрения, мне просто любопытно, в каких ситуациях / приложениях используется эта концепция? Если у вас есть практические примеры, поделитесь, пожалуйста. Это очень помогло бы понять эту концепцию еще лучше. - person Milan; 22.08.2020

class A 
{
    public:
       int x;
    protected:
       int y;
    private:
       int z;
};

class B : public A
{
    // x is public
    // y is protected
    // z is not accessible from B
};

class C : protected A
{
    // x is protected
    // y is protected
    // z is not accessible from C
};

class D : private A    // 'private' is default for classes
{
    // x is private
    // y is private
    // z is not accessible from D
};

ВАЖНОЕ ПРИМЕЧАНИЕ: Классы B, C и D содержат переменные x, y и z. Это просто вопрос доступа.

Об использовании защищенного и частного наследования вы можете прочитать здесь.

person Kirill V. Lyadvinsky    schedule 03.09.2009
comment
То, что написал Anzurio, было выбрано только вместе с вашим ответом, приведенным ниже. Плус 1. - person Iwillnotexist Idonotexist; 04.03.2015
comment
Мое понимание того, как это работает, было ТАК ДАЛЬШЕ! Большое спасибо за разъяснения. - person tjwrona1992; 03.08.2017
comment
мне потребовалось некоторое время, чтобы понять это. Но теперь все ясно. Спасибо! - person Chan Kim; 26.04.2019
comment
Хорошее замечание о том, что "частный" является значением по умолчанию для классов. - person John Leuenhagen; 06.07.2020
comment
Прекрасное объяснение. Пока здесь все ясно. - person Badhan Sen; 16.06.2021
comment
Существует более подробное объяснение с примерами: programiz.com/cpp-programming /. - person Wade Wang; 30.06.2021

Ограничение видимости наследования приведет к тому, что код не сможет увидеть, что какой-то класс наследует другой класс: неявные преобразования из производного в базовый не будут работать, и static_cast из базового в производное тоже не будет работать.

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

публичное наследование

  1. IS-Наследование. Кнопка - это окно, и везде, где необходимо окно, кнопку тоже можно передать.

    class button : public window { };
    

защищенное наследование

  1. Защищено реализовано в терминах. Редко полезно. Используется в boost::compressed_pair для наследования пустых классов и экономии памяти с использованием оптимизации пустого базового класса (в приведенном ниже примере не используется шаблон, чтобы оставаться в точке):

    struct empty_pair_impl : protected empty_class_1 
    { non_empty_class_2 second; };
    
    struct pair : private empty_pair_impl {
      non_empty_class_2 &second() {
        return this->second;
      }
    
      empty_class_1 &first() {
        return *this; // notice we return *this!
      }
    };
    

частное наследование

  1. Реализовано в терминах. Базовый класс используется только для реализации производного класса. Полезно с признаками и если размер имеет значение (пустые признаки, которые содержат только функции, будут использовать оптимизацию пустого базового класса). Однако зачастую сдерживание - лучшее решение. Размер строк имеет решающее значение, поэтому здесь часто встречается

    template<typename StorageModel>
    struct string : private StorageModel {
    public:
      void realloc() {
        // uses inherited function
        StorageModel::realloc();
      }
    };
    

общедоступный участник

  1. Совокупный

    class pair {
    public:
      First first;
      Second second;
    };
    
  2. Аксессоры

    class window {
    public:
        int getWidth() const;
    };
    

защищенный участник

  1. Обеспечение расширенного доступа для производных классов

    class stack {
    protected:
      vector<element> c;
    };
    
    class window {
    protected:
      void registerClass(window_descriptor w);
    };
    

частный участник

  1. Сохраняйте детали реализации

    class window {
    private:
      int width;
    };
    

Обратите внимание, что приведение типов в стиле C специально позволяет преобразовывать производный класс в защищенный или частный базовый класс определенным и безопасным образом, а также выполнять приведение в другом направлении. Этого следует избегать любой ценой, потому что это может сделать код зависимым от деталей реализации, но при необходимости вы можете использовать эту технику.

person Johannes Schaub - litb    schedule 03.09.2009
comment
Я думаю, что Скотту Майерсу (насколько мне нравятся его материалы) есть что ответить за общую путаницу. Теперь я думаю, что его аналогий IS-A и IS-РЕАЛИЗАЦИЯ-В-УСЛОВИЯХ достаточно для того, что происходит. - person DangerMouse; 14.09.2012

Эти три ключевых слова также используются в совершенно другом контексте для определения модели наследования видимости.

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

введите описание изображения здесь

Приведенная выше таблица интерпретируется следующим образом (взгляните на первую строку):

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

Пример:

 class Super {
    public:      int p;
    private:     int q;
    protected:   int r;
 };

 class Sub : private Super {};

 class Subsub : public Sub {};

В результате доступ к переменным p, q, r в классе Subsub равен нет.

Другой пример:

class Super {
    private:     int x;
    protected:   int y;
    public:      int z;
 };
class Sub : protected Super {};

В результате доступ к переменным y, z в классе Sub защищен, а для переменной x отсутствует.

Более подробный пример:

class Super {
private:
    int storage;
public:
    void put(int val) { storage = val;  }
    int  get(void)    { return storage; }
};
int main(void) {
    Super object;

    object.put(100);
    object.put(object.get());
    cout << object.get() << endl;
    return 0;
}

Теперь давайте определим подкласс:

class Sub : Super { };

int main(void) {
    Sub object;

    object.put(100);
    object.put(object.get());
    cout << object.get() << endl;
    return 0;
}

Определенный класс с именем Sub, который является подклассом класса с именем Super или этот класс Sub является производным от класса Super. Класс Sub не вводит ни новых переменных, ни новых функций. Означает ли это, что любой объект класса Sub наследует все черты после того, как класс Super фактически является копией объектов класса Super?

Нет. Это не так.

Если мы скомпилируем следующий код, мы не получим ничего, кроме ошибок компиляции, говорящих о том, что методы put и get недоступны. Почему?

Когда мы опускаем спецификатор видимости, компилятор предполагает, что мы собираемся применить так называемое частное наследование. Это означает, что все компоненты общедоступного суперкласса превращаются в частный доступ, а частные компоненты суперкласса не будут доступны вообще. Следовательно, это означает, что вам не разрешено использовать последний внутри подкласса.

Мы должны сообщить компилятору, что хотим сохранить ранее используемую политику доступа.

class Sub : public Super { };

Не обманывайтесь: это не означает, что частные компоненты суперкласса (например, переменная хранилища) каким-то волшебным образом превратятся в общедоступные. Частные компоненты останутся частными, общедоступными останутся общедоступными.

Объекты класса Sub могут выполнять «почти» те же действия, что и их более старые братья и сестры, созданные из класса Super. «Почти», потому что факт принадлежности к подклассу также означает, что класс потерял доступ к частным компонентам суперкласса. Мы не можем написать функцию-член класса Sub, которая могла бы напрямую управлять переменной хранилища.

Это очень серьезное ограничение. Есть ли обходной путь?

Да.

Третий уровень доступа называется защищенным. Ключевое слово protected означает, что отмеченный им компонент ведет себя как общедоступный при использовании любым из подклассов и выглядит как частный для остального мира. - Это верно только для публично унаследованных классов (например, суперкласса в нашем примере) -

class Super {
protected:
    int storage;
public:
    void put(int val) { storage = val;  }
    int  get(void)    { return storage; }
};

class Sub : public Super {
public:
    void print(void) {cout << "storage = " << storage;}
};

int main(void) {
    Sub object;

    object.put(100);
    object.put(object.get() + 1);
    object.print();
    return 0;
}

Как вы видите в примере кода, мы добавили новую функциональность к классу Sub, и он делает одну важную вещь: он получает доступ к переменной хранилища из суперкласса.

Было бы невозможно, если бы переменная была объявлена ​​частной. В области основной функции переменная все равно остается скрытой, поэтому, если вы напишете что-нибудь вроде:

object.storage = 0;

Компилятор сообщит вам, что это error: 'int Super::storage' is protected.

Наконец, последняя программа выдаст следующий результат:

storage = 101
person BugShotGG    schedule 07.06.2015
comment
Первый, кто упомянул об отсутствии модификатора (как в Class: SuperClass), дает частный. Это важная часть, которую упускают другие, наряду с подробными объяснениями. +1 - person Water; 25.10.2016
comment
Перебил ИМО, но мне нравится таблица в начале. - person cp.engr; 04.08.2018

Это связано с тем, как открытые члены базового класса предоставляются из производного класса.

  • public -> base class's public members will be public (usually the default)
  • protected -> base class's public members will be protected
  • private -> base class's public members will be private

Как указывает литб, публичное наследование - это традиционное наследование, которое вы увидите в большинстве языков программирования. То есть моделирует отношения "IS-A". Частное наследование, что-то, что, как мне кажется, свойственно C ++, является отношением «РЕАЛИЗОВАНО В УСЛОВИЯХ». То есть вы хотите использовать открытый интерфейс в производном классе, но не хотите, чтобы пользователь производного класса имел доступ к этому интерфейсу. Многие утверждают, что в этом случае вы должны агрегировать базовый класс, то есть вместо того, чтобы иметь базовый класс в качестве частной базы, сделать член производного, чтобы повторно использовать функциональность базового класса.

person Doug T.    schedule 13.05.2009
comment
Лучше сказать публично: наследство увидят все. protected: наследование будет видно только производным классам и друзьям, private: наследование будет видно только самому классу и друзьям. Это отличается от вашей формулировки, поскольку не только члены могут быть невидимыми, но и отношение IS-A может быть невидимым. - person Johannes Schaub - litb; 14.05.2009
comment
Один раз я использовал частное наследование, чтобы сделать именно то, что описывает Дуг Т., то есть вы хотите использовать открытый интерфейс в производном классе, но не хотите, чтобы пользователь производного класса имел доступ к этому интерфейсу. Я в основном использовал его, чтобы изолировать старый интерфейс и открыть другой через производный класс. - person Rich; 23.04.2010

Member in base class : Private   Protected   Public   

Тип наследования: Объект, унаследованный как:

Private            :   Inaccessible   Private     Private   
Protected          :   Inaccessible   Protected   Protected  
Public             :   Inaccessible   Protected   Public
person kinshuk4    schedule 09.09.2010
comment
Это заблуждение. Частные члены базового класса ведут себя совершенно иначе, чем обычные частные члены класса - они вообще недоступны из производного класса. Я думаю, ваша колонка из трех Private должна быть колонкой Inaccessible. См. Ответ Кирилла Лядвинского на этот вопрос. - person Sam Kauffman; 12.04.2013

1) Общедоступное наследование:

а. Закрытые члены базового класса недоступны в производном классе.

б. Защищенные члены базового класса остаются защищенными в производном классе.

c. Открытые члены базового класса остаются открытыми в производном классе.

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

2) Защищенное наследование:

а. Закрытые члены базового класса недоступны в производном классе.

б. Защищенные члены базового класса остаются защищенными в производном классе.

c. Открытые члены базового класса тоже становятся защищенными членами производного класса.

Таким образом, другие классы не могут использовать открытые члены базового класса через объект производного класса; но они доступны для подкласса Derived.

3) Частное наследование:

а. Закрытые члены базового класса недоступны в производном классе.

б. Защищенные и открытые члены базового класса становятся частными членами производного класса.

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

person yuvi    schedule 26.05.2016

Публичное наследование моделирует отношения IS-A. С участием

class B {};
class D : public B {};

каждый D является B.

Частное наследование моделирует отношения IS-IMPLEMENTED-USING (или как там это называется). С участием

class B {};
class D : private B {};

D не B, но каждый D использует свой B в своей реализации. Частное наследование всегда можно исключить, используя вместо него сдерживание:

class B {};
class D {
  private: 
    B b_;
};

Этот D тоже может быть реализован с помощью B, в данном случае с его b_. Сдерживание - это менее тесная связь между типами, чем наследование, поэтому в целом ему следует предпочесть. Иногда использование включения вместо частного наследования не так удобно, как частное наследование. Часто это неубедительное оправдание лени.

Я не думаю, что кто-то знает, какие protected модели наследования. По крайней мере, убедительного объяснения я пока не видел.

person sbi    schedule 03.09.2009
comment
Некоторые говорят, что это отношения. Как использовать стул как молоток. Здесь стул: защищенный молоток - person user4951; 28.11.2013
comment
когда использование включения вместо частного наследования не так удобно, как частное наследование? Не могли бы вы объяснить это на примере? - person Destructor; 06.11.2015
comment
@Pravasi: Если D является производным от D, он может переопределить виртуальные функции B. (Если, например, B является интерфейсом наблюдателя, то D может реализовать его и передать this функциям, требующим какой-либо интерфейс, без возможности использовать D в качестве наблюдателя.) Кроме того, D может выборочно сделать элементы B доступными в свой интерфейс, выполнив using B::member. Оба варианта синтаксически неудобны для реализации, когда B является членом. - person sbi; 06.11.2015
comment
@sbi: старый, но ... сдерживание недопустимо в случае CRTP и / или виртуалов (как вы правильно описали в комментарии, но это означает, что его нельзя смоделировать как сдерживание, если у B есть абстрактные методы, а вы нельзя трогать его). protected наследование, которое я нашел полезным с virtual базовым классом и protected ctor: struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} }; - person lorro; 08.04.2017

Если вы публично наследуете от другого класса, все знают, что вы наследуете, и вы можете полиморфно использовать кем угодно через указатель базового класса.

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

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

Что в основном символизирует знания остальных классов о ваших отношениях с родительским классом.

person Arkaitz Jimenez    schedule 03.09.2009

Доступ к защищенным элементам данных могут получить любые классы, унаследованные от вашего класса. Однако члены с частными данными не могут. Допустим, у нас есть следующее:

class MyClass {
    private:
        int myPrivateMember;    // lol
    protected:
        int myProtectedMember;
};

Изнутри вашего расширения к этому классу ссылка на this.myPrivateMember не будет работать. Однако this.myProtectedMember будет. Значение по-прежнему инкапсулировано, поэтому, если у нас есть экземпляр этого класса с именем myObj, то myObj.myProtectedMember не будет работать, поэтому он аналогичен функции частного члена данных.

person Andrew Noyes    schedule 13.05.2009

Accessors    | Base Class | Derived Class | World
—————————————+————————————+———————————————+———————
public       |      y     |       y       |   y
—————————————+————————————+———————————————+———————
protected    |      y     |       y       |   n
—————————————+————————————+———————————————+———————
private      |            |               |    
  or         |      y     |       n       |   n
no accessor  |            |               |

y: accessible
n: not accessible

На основе этого примера для java ... Я думаю, что столик стоит тысячи слов :)

person Enissay    schedule 09.05.2015
comment
Java имеет только публичное наследование - person Zelldon; 12.06.2015
comment
Это не та тема, чтобы говорить о java, но НЕТ, ты ошибаешься ... Для получения подробной информации перейдите по ссылке в моем ответе выше. - person Enissay; 13.06.2015
comment
Вы упомянули Java, так что это тема. И ваш пример обрабатывает спецификаторы, которые используются в jaca. Вопрос в том, что спецификаторы наследования, которых нет в Java, имеют значение. Если поле в суперклассе является общедоступным, а наследование является частным, поле доступно только внутри подкласса. Снаружи нет указания, расширяет ли подкласс суперкласс. Но ваша таблица объясняет только спецификаторы поля и методов. - person Zelldon; 13.06.2015

Резюме:

  • Частный: никто не может видеть это, кроме как внутри класса
  • Защищено: частные + производные классы могут видеть это
  • Публика: мир это видит

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

person Roee Adler    schedule 13.05.2009

Частный:

Доступ к закрытым членам базового класса могут получить только члены этого базового класса.

Публичный:

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

Защищено:

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


Суммируя:

частный: базовый

защищенный: базовый + производный

общедоступный: базовый + производный + любой другой член

person varun    schedule 06.01.2010

Я попытался объяснить наследование, используя картинку ниже.

Основная суть заключается в том, что частные члены родительского класса никогда не доступны напрямую из производного / дочернего класса, но вы можете использовать функцию члена родительского класса для доступа к частным членам родительского класса. Частные переменные всегда присутствуют в производном классе, но не могут быть доступны производному классу. Это похоже на их, но вы не можете видеть своими глазами, но если вы спросите кого-нибудь из родительского класса, он сможет вам это описать. Сопоставление наследования cpp

person shubhcodegate    schedule 22.09.2020
comment
На самом деле это лучший способ объяснить доступ к наследованию, который я когда-либо видел. - person Soup Endless; 04.03.2021

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

Его можно найти по ссылкам http://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/

class Base
{
public:
    int m_nPublic; // can be accessed by anybody
private:
    int m_nPrivate; // can only be accessed by Base member functions (but not derived classes)
protected:
    int m_nProtected; // can be accessed by Base member functions, or derived classes.
};

class Derived: public Base
{
public:
    Derived()
    {
        // Derived's access to Base members is not influenced by the type of inheritance used,
        // so the following is always true:

        m_nPublic = 1; // allowed: can access public base members from derived class
        m_nPrivate = 2; // not allowed: can not access private base members from derived class
        m_nProtected = 3; // allowed: can access protected base members from derived class
    }
};

int main()
{
    Base cBase;
    cBase.m_nPublic = 1; // allowed: can access public members from outside class
    cBase.m_nPrivate = 2; // not allowed: can not access private members from outside class
    cBase.m_nProtected = 3; // not allowed: can not access protected members from outside class
}
person Prajosh Premdas    schedule 24.02.2015

По сути, это защита доступа публичных и защищенных членов базового класса в производном классе. При общедоступном наследовании производный класс может видеть общедоступные и защищенные члены базы. С частным наследованием этого не может. С protected производный класс и любые производные классы могут их видеть.

person Dan Olson    schedule 13.05.2009