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

Ответы:

1066

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

Есть три аксессоров , что я в курсе: 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как не знает о наследовании.
Anzurio
источник
183
Я хотел бы добавить несколько слов о том, что видимость в C ++ основана на классе, а не на объекте, что означает, что объекты одного и того же класса могут получать доступ к закрытым полям друг друга без ограничений.
Чже Чен
48
Если вам трудно понять это, прочитайте ответ Кирилла Лядвинского, затем вернитесь и прочитайте это.
Vivandiere
6
Это просто еще один случай, который иллюстрирует, как наследование по большей части SomeBaseпохоже на жестко запрограммированный способ создания анонимного члена типа SomeBase. Это, как и любой другой член, имеет спецификатор доступа, который оказывает такой же контроль на внешний доступ.
underscore_d
1
@ZheChen если у меня есть объекты Том и Джерри из класса Лицо с частным полевым возрастом, как получить (и изменить) возраст Джерри с помощью Тома?
Генерал
2
Не могли бы вы проиллюстрировать, что вы подразумеваете под «осознанием« наследования »»? Я могу понять, «я могу получить доступ к этому, я не могу получить доступ к этому», но я не получаю его, когда кто-то говорит: «Я знаю, что A наследует от B». Что я здесь делаю, проверяю ли я наследование?
neilxdims
1461
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. Это просто вопрос доступа.

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

Кирилл Васильевич Лядвинский
источник
35
То, что написал Анзурио, щелкнуло только в связи с вашим ответом, приведенным ниже. Плус 1.
Ивилнотексист Идонотексист
2
Мое понимание того, как это работает, было так далеко! Большое спасибо за разъяснения.
tjwrona1992
Мне потребовалось некоторое время, чтобы понять это. Но теперь все ясно. Спасибо!
Чан Ким
115

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

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

общественности наследство

  1. ИС-А наследство. Кнопка - это окно, и везде, где требуется окно, кнопка также может быть передана.

    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. Accessors

    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 преднамеренно позволяют приводить производный класс к защищенному или частному базовому классу определенным и безопасным способом, а также приводить в другом направлении. Этого следует избегать любой ценой, потому что это может сделать код зависимым от деталей реализации - но при необходимости вы можете использовать эту технику.

Йоханнес Шауб - Литб
источник
7
Я думаю, что Скотту Майерсу (как бы мне ни нравились его вещи) есть, что ответить за общее замешательство. Теперь я думаю, что его аналогии IS-A и IS-IMPLEMENTED-IN-TERMS-OF достаточно для того, что происходит.
DangerMouse
65

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

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

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

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

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

Пример:

 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 { };

Не вводите в заблуждение : это не означает, что частные компоненты класса 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класс новую функциональность, и она делает одну важную вещь: она обращается к переменной хранения из класса Super .

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

object.storage = 0;

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

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

storage = 101
BugShotGG
источник
4
Первый упомянуть отсутствие модификатора (как в Class: SuperClass) приводит к приватности. Это важная часть, которую упускают другие, наряду с подробными объяснениями. +1
вода
2
Избыток ИМО, но мне нравится таблица в начале.
cp.engr
63

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

  • public -> публичные члены базового класса будут публичными (обычно по умолчанию)
  • protected -> публичные члены базового класса будут защищены
  • private -> публичные члены базового класса будут приватными

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

Дуг Т.
источник
13
Лучше сказать «публично: наследство увидят все». Защищено: наследование будет видеть только производные классы и друзья "," личное: наследование будет видеть только сам класс и друзья ". Это отличается от вашей формулировки, поскольку не только члены могут быть невидимыми, но и отношение IS-A может быть невидимым
Йоханнес Шауб -
4
Один раз, когда я использовал частное наследование, я делал только то, что описывает Дуг Т, т.е. «вы хотите использовать открытый интерфейс в производном классе, но не хотите, чтобы пользователь производного класса имел доступ к этому интерфейсу». Я в основном использовал его, чтобы закрыть старый интерфейс и показать другой через производный класс.
Рич
36
Member in base class : Private   Protected   Public   

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

Private            :   Inaccessible   Private     Private   
Protected          :   Inaccessible   Protected   Protected  
Public             :   Inaccessible   Protected   Public
kinshuk4
источник
23
Это вводит в заблуждение. Частные члены базового класса ведут себя совершенно иначе, чем обычные члены частного класса - они вообще недоступны из производного класса. Я думаю, что ваша колонка из трех "Приват" должна быть колонкой "Недоступно". См. Ответ Кирилла В. Лядвинского на этот вопрос.
Сэм Кауффман
27

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Yuvi
источник
20

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

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

каждый D есть B .

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

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

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

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

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

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

SBI
источник
Некоторые говорят как отношения. Как использовать стул в качестве молотка. Вот кресло: защищенный молоток
user4951
при использовании содержания вместо частного наследования не так удобно, как частное наследование? Не могли бы вы объяснить это на примере?
Разрушитель
@Pravasi: Если Dпроисходит от частного D, он может переопределить виртуальные функции B. (Если, например, Bэто интерфейс наблюдателя, то он Dможет реализовать его и перейти thisк функциям, требующим наличия интерфейса, при этом каждый не сможет использовать его Dв качестве наблюдателя.) Кроме того, Dможно выборочно сделать членов Bдоступными в его интерфейсе, выполняя using B::member. Оба синтаксически неудобно для реализации, когда Bявляется членом.
СБИ
@sbi: старый, но ... сдерживание - это запрет на использование CRTP и / или виртуалов (как вы правильно описали в комментарии - но это означает, что его нельзя смоделировать как сдерживание, если у B есть абстрактные методы и вы не разрешается прикасаться к нему). protectedЯ нашел полезное наследование с virtualбазовым классом и protectedctor:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
lorro
11

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

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

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

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

Аркаитц Хименес
источник
9

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

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

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

Эндрю Нойес
источник
8
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 ... Я думаю, что столик стоит тысячу слов :)

Enissay
источник
У Java есть только публичное наследство
Зеллдон,
Это не тема, чтобы говорить о Java, но НЕТ, вы не правы ... Перейдите по ссылке в моем ответе выше для деталей
Enissay
Вы упомянули Java, так что это тема. И ваш пример обрабатывает спецификаторы, которые используются в jaca. Речь идет о спецификаторах для наследования, которые не существуют в Java и сделали разницу. Если поле в суперклассе является общедоступным, а наследование является частным, поле доступно только внутри подкласса. Снаружи нет указания, расширяет ли подкласс суперкласс. Но ваша таблица объясняет только спецификаторы для поля и методов.
Зеллдон
7

Резюме:

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

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

Роуи Адлер
источник
6

Частный:

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

Общественность:

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

Защищено:

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


Короче говоря:

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

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

public : base + производная + любой другой участник

Варуна
источник
5

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

Это из ссылок 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
}
Праджош Премдас
источник
3

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

Дэн Олсон
источник