Різниця між приватним, громадським та захищеним спадщиною


Відповіді:


1064

Щоб відповісти на це запитання, я хотів би схарактеризувати спочатку своїх учасників. Якщо ви вже знаєте це, перейдіть до заголовка "наступний:".

Є три аксессор , що я в курсі: 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знає ніхто інший, окрім як.

182
Я хотів би додати кілька слів, що видимість у C ++ заснована на класі, а не на об'єкті, а це означає, що об’єкти одного класу можуть без обмежень отримувати доступ до приватних полів один одного.
Чже Чень

48
Якщо вам важко це зрозуміти, прочитайте відповідь Кирила В. Лядвинського, а потім поверніться і прочитайте це.
The Vivandiere

6
Це лише черговий випадок, який ілюструє, як правило, успадкування з боку SomeBase- це як жорсткий спосіб складання анонімного члена типу SomeBase. Цей, як і будь-який інший член, має специфікатор доступу, який здійснює той же контроль над зовнішнім доступом.
підкреслюйте_d

1
@ZheChen якщо у мене є об'єкти Том і Джеррі класу Особа з приватним віком, як ви отримуєте доступ (і змінюєте?) Вік Джеррі, використовуючи Тома?
рід

2
Чи можете ви проілюструвати, що ви маєте на увазі під «усвідомленням спадщини»? Я можу зрозуміти "я можу отримати доступ до цього, я не можу отримати доступ до цього", але я не отримую цього, коли хтось каже "я знаю, що A успадковує від B", що я тут роблю, чи перевіряю спадщину?
neilxdims

1458
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.
Iwillnotexist Idonotexist

2
Моє розуміння того, як це працювало, було ТАКЕ ДІЙСНО! Дуже дякую за уточнення.
tjwrona1992

мені знадобилося певний час, щоб зрозуміти це. Але тепер зрозуміло. Дякую!
Чан Кім

115

Обмеження видимості успадкування призведе до того, що код не зможе побачити, що деякий клас успадковує інший клас: невідповідні перетворення від похідного до бази не працюватимуть, і static_castвід бази до похідного також не буде працювати.

Лише члени / друзі класу можуть бачити приватне успадкування, і лише члени / друзі та похідні класи можуть бачити захищене спадкування.

публічна спадщина

  1. IS-A спадщина. Кнопка - це вікно, і в будь-якому місці, де потрібне вікно, також можна пропустити кнопку.

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


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класу, яка могла б безпосередньо маніпулювати змінною зберігання.

Це дуже серйозне обмеження. Чи є якесь вирішення?

Так .

Третій рівень доступу називається захищеним . Захищене ключове слово означає, що позначений ним компонент поводиться як загальнодоступний при використанні будь-якого з підкласів і схожий на приватний на інший світ . - Це справедливо лише для публічно успадкованих класів (як, наприклад, клас Super у нашому прикладі) -

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

4
Перший, щоб згадати відсутність модифікатора (як у Class: SuperClass), дає приватний характер. Це важливий фрагмент, який інші пропускають, а також ґрунтовні пояснення. +1
Вода

2
Перевищення IMO, але мені подобається таблиця на початку.
cp.engr

63

Це стосується того, як громадські члени базового класу піддаються впливу похідного класу.

  • public -> Публічні члени базового класу будуть загальнодоступними (зазвичай за замовчуванням)
  • захищені -> громадські члени базового класу будуть захищені
  • приватний -> Публічні члени базового класу будуть приватними

Як вказує litb, публічне успадкування - це традиційне успадкування, яке ви побачите в більшості мов програмування. Тобто це моделює відносини "IS-A". Приватне успадкування, що є властивим для C ++, є AFAIK - це відносини "ВПРОВАДЖЕНО У УМОВИ". Тобто ви хочете використовувати публічний інтерфейс у похідному класі, але не хочете, щоб користувач похідного класу мав доступ до цього інтерфейсу. Багато хто стверджує, що в цьому випадку вам слід об'єднати базовий клас, тобто замість того, щоб базовий клас був приватним, вносити в член похідний, щоб повторно використовувати функціональність базового класу.


13
Краще скажіть "громадськість: спадщину побачать усі". захищено: спадщину бачитимуть лише похідні класи та друзі "," приватні: спадщину бачитимуть лише сам клас та друзі ". Це відрізняється від вашого формулювання, оскільки не лише члени можуть бути невидимими, але й відносини IS-A можуть бути невидимими
Йоханнес Шауб - засвіт

4
Одного разу, коли я використовував приватне успадкування, було робити саме те, що описує Дуг Т, тобто "ви хочете використовувати публічний інтерфейс у похідному класі, але не хочете, щоб користувач похідного класу мав доступ до цього інтерфейсу". Я в основному використовував його для герметизації старого інтерфейсу та викриття іншого через похідний клас.
Багатий

36
Member in base class : Private   Protected   Public   

Тип успадкування :              об’єкт успадковується як :

Private            :   Inaccessible   Private     Private   
Protected          :   Inaccessible   Protected   Protected  
Public             :   Inaccessible   Protected   Public

23
Це введення в оману. Приватні члени базового класу поводяться зовсім інакше, ніж звичайні члени приватного класу - вони взагалі недоступні для похідного класу. Я думаю, що ваша колонка з трьох "Приватних" має бути стовпцем "Недоступне". Дивіться відповідь Кирила В. Лядвинського на це запитання.
Сем Кауффман

27

1) Громадське успадкування :

а. Приватні члени базового класу недоступні в класі Похідні.

б. Захищені члени базового класу залишаються захищеними в класі похідних.

c. Громадські члени базового класу залишаються відкритими в класі похідних.

Отже, інші класи можуть використовувати публічних членів класу Base через об’єкт класу Derived.

2) Захищене спадкування :

а. Приватні члени базового класу недоступні в класі Похідні.

б. Захищені члени базового класу залишаються захищеними в класі похідних.

c. Громадські члени базового класу теж стають захищеними членами класу Derived.

Отже, інші класи не можуть використовувати публічних членів класу Base через об’єкт Derived class; але вони доступні для підкласу Похідне.

3) Приватне успадкування :

а. Приватні члени базового класу недоступні в класі Похідні.

б. Захищені та громадські члени базового класу стають приватними членами класу "Похідні".

Отже, жоден член класу Base не може отримати доступ до інших класів через об’єкт класу Derived, оскільки вони є приватними у класі Derived. Отже, навіть підклас класу Derived не може отримати до них доступ.


20

Суспільна модель успадкування відносин IS-A. З

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

кожен D - a B .

Приватні моделі успадкування стосунків, які ВИМОГАЮТЬ-ВИКОРИСТОВУЮТЬСЯ (або як це називається). З

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

Dце НЕB , але кожен Dвикористовує Bв своїй реалізації. Приватне успадкування завжди можна усунути, використовуючи замість цього:

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

Це Dтакож можна реалізувати, використовуючи B, у цьому випадку, його b_. Утримання - це менш тісний зв'язок між типами, ніж спадкування, тому загалом слід віддати перевагу. Іноді використовувати стримування замість приватного успадкування не так зручно, як приватне успадкування. Часто це кульгавий привід за те, що лінивий.

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


Деякі кажуть, як відносини. Любіть використовувати крісло як молоток. Тут стілець: захищений молот
user4951

коли використання стримування замість приватного спадкування не так зручне, як приватне успадкування? Чи поясніть, будь ласка, на прикладі?
Руйнівник

@ Pravasi: Якщо Dприватно походить від D, воно може змінювати віртуальні функції B. (Якщо, наприклад, Bце інтерфейс для спостерігачів, тоді він Dможе реалізовувати його і переходити thisдо функцій, що вимагають аукціону інтерфейсу, не маючи змоги використовувати його Dяк спостерігач.) Також, він Dможе вибірково зробити членів Bдоступними в його інтерфейсі, роблячи це using B::member. Обидва синтаксично незручно реалізовувати, коли Bє членом.
sbi

@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 ... Я думаю, що маленький стіл вартістю тисячі слів :)


Ява має лише публічну спадщину
Целдон

Це не тема, щоб говорити про Java, але НЕ, ви помиляєтесь ... Докладно
переходьте

Ви згадали Java, тож це тема. І ваш приклад обробляє специфікатори, які використовуються в jaca. Питання стосується специфікаторів успадкування, яких у Java немає, а змінився. Якщо поле в надкласі є загальнодоступним, а спадкування є приватним, поле є доступним лише підкласом. Зовні немає вказівки, якщо підклас розширює надклас. Але ваша таблиця пояснює лише специфікатори для поля та методів.
Зелдон

7

Підсумок:

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

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


6

Приватний:

До приватних членів базового класу можуть звертатися лише члени цього базового класу.

Загальнодоступне:

До публічних членів базового класу можуть звертатися члени цього базового класу, члени його похідного класу, а також члени, які знаходяться поза базовим класом і похідним класом.

Захищено:

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


Коротко:

приватний : база

захищено : база + похідне

громадський : база + похідне + будь-який інший член


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

По суті це захист доступу для публіки та захищених членів базового класу у похідному класі. Маючи державну спадщину, похідний клас може бачити публічних та захищених членів бази. При приватному успадкуванні це не може. З захищеним, похідним класом і будь-якими класами, що випливають з цього, можна побачити їх.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.