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

826

В чем разница между public, private и protected наследованием в С++? Все вопросы, которые я нашел на SO, касаются конкретных случаев.

Теги:
inheritance
encapsulation
access-specifier
c++-faq

16 ответов

956
Лучший ответ

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

Есть три устройства доступа, о которых я знаю: 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 о наследовании.
  • 159
    Я хотел бы добавить несколько слов о том, что видимость в C ++ основана на классе, а не на объекте, что означает, что объекты одного класса могут получать доступ к закрытым полям друг друга без ограничений.
  • 37
    Если вам трудно понять это, прочитайте ответ Кирилла Лядвинского, а затем вернитесь и прочитайте это.
Показать ещё 10 комментариев
1293
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. Это просто вопрос доступа.

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

  • 27
    То, что написал Анзурио, щелкнуло только в связи с вашим ответом, приведенным ниже. Плус 1.
  • 2
    Мое понимание того, как это работает, было так далеко! Большое спасибо за разъяснения.
Показать ещё 1 комментарий
95

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

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

public Наследование

  • Наследование IS-A. Кнопка - это окно, и в любом месте, где требуется окно, кнопка также может быть передана.

    class button : public window { };
    

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

  • Защищенные встроенные функции. Редко полезно. Используется в 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!
      }
    };
    

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

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

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

публичный участник

  • Совокупные

    class pair {
    public:
      First first;
      Second second;
    };
    
  • Accessors

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

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

  • Предоставление расширенного доступа для производных классов

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

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

  • Сведения о реализации

    class window {
    private:
      int width;
    };
    

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

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

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

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

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

  • 13
    Лучше сказать «публично: наследство увидят все». Защищено: наследование будут видеть только производные классы и друзья "," Приватное: наследование будут видеть только сам класс и друзья ". Это отличается от вашей формулировки, поскольку не только члены могут быть невидимыми, но и отношение IS-A может быть невидимым.
  • 4
    Один раз, когда я использовал частное наследование, я делал только то, что описывает Дуг Т, т. Е. «Вы хотите использовать открытый интерфейс в производном классе, но не хотите, чтобы пользователь производного класса имел доступ к этому интерфейсу». Я в основном использовал его, чтобы запечатать старый интерфейс и выставить другой через производный класс.
41

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

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

Изображение 1197

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

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

Пример:

 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
  • 2
    Первый упомянуть отсутствие модификатора (как в Class: SuperClass) приводит к приватности. Это важная часть, которую упускают другие, наряду с подробными объяснениями. +1
  • 1
    Избыток ИМО, но мне нравится таблица в начале.
Показать ещё 1 комментарий
33
Member in base class : Private   Protected   Public   

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

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

1) Публичное наследование:

а. Частные члены базового класса недоступны в классе Derived.

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

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

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

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

а. Частные члены базового класса недоступны в классе Derived.

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

с. Публичные члены базового класса тоже становятся защищенными членами класса Derived.

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

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

а. Частные члены базового класса недоступны в классе Derived.

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

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

19

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

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

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

Частное наследование моделирует отношения IS-IMPLEMENTED-USING (или что-то другое). С помощью

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

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

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

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

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

  • 0
    Некоторые говорят как отношения. Как использовать стул в качестве молотка. Здесь кресло: защищенный молот
  • 0
    когда использование локализации вместо частного наследования не так удобно, как частное наследование? Не могли бы вы объяснить это на примере?
Показать ещё 2 комментария
12

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

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

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

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

9

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

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

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

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

  • 0
    Java имеет только публичное наследование
  • 0
    Это не тема, чтобы говорить о Java, но нет, вы не правы ... Перейдите по ссылке в моем ответе выше для деталей
Показать ещё 1 комментарий
7

Резюме:

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

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

6

Частный:

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

Public:

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

Защищено:

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


Короче:

закрытый: базовый

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

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

4

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

Из ссылок 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 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

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

-6

Смотрите эти коды, чтобы понять особенности С++ о наследовании... Я положил результат в конце... Надеюсь, что это поможет.

#include <iostream>
using namespace std;

class A {
private:
    void pri();
    A(int a);
protected:
    virtual void pro() {}
public:
    void pub1() { cout<<"A.pub1()\n"; }
    virtual void pub2() { cout<<"A.pub2()\n"; }
    virtual void pub3() { cout<<"A.pub3()\n"; }
    virtual void pub4() { cout<<"A.pub4()\n"; }
    virtual void pub5() { cout<<"A.pub5()\n"; }
    virtual void pub6() { cout<<"A.pub6()\n"; }
    virtual void pub7() { cout<<"A.pub7()\n"; }
    virtual void pub8() { cout<<"A.pub8()\n"; }
    void pub9() { cout<<"A.pub9()\n"; }
    virtual void pub10() { cout<<"A.pub10()\n"; }
    void pub11() { cout<<"A.pub11()\n"; }
    explicit A() {}
    virtual ~A() {}
};

class B : public A {
private:
    void pri() { cout<<"B.pri()\n"; }
protected:
    virtual void pub4() { cout<<"B.pub4()\n"; }
    void pub6() { cout<<"B.pub6()\n"; }
public:
    void pro() { cout<<"B.pro() "; B::pri(); }
    void pub1() { cout<<"B.pub1()\n"; }
    void pub2() { cout<<"B.pub2()\n"; }
    void pub5() { cout<<"B.pub5()\n"; }
    virtual void pub7() { cout<<"B.pub7()\n"; }
    virtual void pub8() { cout<<"B.pub8()\n"; }
    virtual void pub9() { cout<<"B.pub9()\n"; }
    void pub10() { cout<<"B.pub10()\n"; }
    void pub11() { cout<<"B.pub11()\n"; }
    explicit B() {}
};

class C : protected B {
public:
    void pub4_() { cout<<"C.pub4_() "; B::pub4(); }
    virtual void pub5() { cout<<"C.pub5()\n"; }
};

class D : private B {
public:
    void pub4_() { cout<<"D.pub4_() "; B::pub4(); }
};

class E : public B {
public:
    virtual void pub4() { cout<<"E.pub4()\n"; }
    virtual void pub7() { cout<<"E.pub7()\n"; }
    virtual void pub8() { cout<<"E.pub8()\n"; }
    virtual void pub9() { cout<<"E.pub9()\n"; }
    virtual void pub10() { cout<<"E.pub10()\n"; }
    virtual void pub11() { cout<<"E.pub11()\n"; }
};

void testClasses() {
    A* ap=new B();
    ap->pub1(); // == A::pub1() //important
    // (new B()).pub1() can't override non-virtual A::pub1() for an A* pointer.
    ap->pub2(); // == B::pub2() //important
    // (new B()).pub1() can override virtual A::pub1() for an A* pointer.
    B b;
    b.A::pub1();
    b.pro();
    B* bp=new B;
    bp->pub3();
    C c;
    //c.pub3(); //error
    //c.pub4(); //error
    c.pub4_();
    c.pub5();
    D d;
    //d.pub3(); //error
    //d.pub4(); //error
    d.pub4_();
    E e;
    //e.pub4(); //error
    delete ap;
    ap = new E();
    ap->pub4();
    ap->pub5();
    ap->pub6();
    ap->pub7();
    delete bp;
    bp = new E();
    e.pub8();
    e.A::pub8();
    e.B::A::pub8();
    e.B::pub8();
    ap->pub8();
    bp->pub8();
    e.pub9();
    e.A::pub9();
    e.B::A::pub9();
    e.B::pub9();
    ap->pub9(); // important
    bp->pub9();
    e.pub10();
    e.A::pub10();
    e.B::A::pub10();
    e.B::pub10();
    ap->pub10(); // important
    bp->pub10(); // very important... eventhough B::pub10() is non-virtual,
                 // bp->pub10() != b.pub10();
    e.pub11();
    e.A::pub11();
    e.B::A::pub11();
    e.B::pub11();
    ap->pub11();
    bp->pub11();
    delete ap;
    delete bp;
    return;
}

int main() {
    testClasses();
    return 0;
}









/////////////////////////////////////////
........
Result :
........

A.pub1()
B.pub2()
A.pub1()
B.pro() B.pri()
A.pub3()
C.pub4_() B.pub4()
C.pub5()
D.pub4_() B.pub4()
E.pub4()
B.pub5()
B.pub6()
E.pub7()
E.pub8()
A.pub8()
A.pub8()
B.pub8()
E.pub8()
E.pub8()
E.pub9()
A.pub9()
A.pub9()
B.pub9()
A.pub9()
E.pub9()
E.pub10()
A.pub10()
A.pub10()
B.pub10()
E.pub10()
E.pub10()
E.pub11()
A.pub11()
A.pub11()
B.pub11()
A.pub11()
B.pub11()

Ещё вопросы

Сообщество Overcoder
Наверх
Меню