Разница между частным, публичным и защищенным наследством - PullRequest
917 голосов
/ 14 мая 2009

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

Ответы [ 15 ]

1345 голосов
/ 03 сентября 2009
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. Это просто вопрос доступа.

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

981 голосов
/ 14 мая 2009

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

Мне известны три метода доступа: 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 не знает о наследовании.
105 голосов
/ 03 сентября 2009

Ограничение видимости наследования сделает код не способным увидеть, что какой-то класс наследует другой класс: неявные преобразования из производного в базовое не будут работать, и 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();
      }
    };
    

public member

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

62 голосов
/ 14 мая 2009

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

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

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

57 голосов
/ 07 июня 2015

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

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

enter image description here

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

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

Пример:

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

 class Sub : private Super {};

 class Subsub : public Sub {};

Полученный доступ к переменным p, q, r в классе Subsub равен none .

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

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
35 голосов
/ 09 сентября 2010
Member in base class : Private   Protected   Public   

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

Private            :   Inaccessible   Private     Private   
Protected          :   Inaccessible   Protected   Protected  
Public             :   Inaccessible   Protected   Public
24 голосов
/ 26 мая 2016

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

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

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

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

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

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

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

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

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

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

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

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

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

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

19 голосов
/ 03 сентября 2009

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

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

каждые D - это B.

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

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

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

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

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

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

12 голосов
/ 03 сентября 2009

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

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

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

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

9 голосов
/ 14 мая 2009

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

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

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

...