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

<p>в чем разница между наследованием <code>public</code>, <code>private</code> и <code>protected</code> в c++? все вопросы, которые я нашел в so, касаются конкретных случаев.</p>

<p>чтобы ответить на этот вопрос, я хотел бы сначала описать методы доступа участников своими словами. если вы уже знаете это, перейдите к заголовку "следующий:".</p><p>мне известны три метода доступа: <code>public</code>, <code>protected</code> и <code>private</code>.</p><p>позволять:</p><pre><code>class base { public: int publicmember; protected: int protectedmember; private: int privatemember; }; </code></pre><ul> <li>все, что знает о <code>base</code>, также знает, что <code>base</code> содержит <code>publicmember</code>.</li> <li>только дети (и их дети) знают, что <code>base</code> содержит <code>protectedmember</code>.</li> <li>никто, кроме <code>base</code>, не знает о <code>privatemember</code>.</li> </ul><p>под "осведомлен" я имею в виду "признать существование и, таким образом, иметь доступ".</p><h2>следующий:</h2><p>то же самое происходит с публичным, частным и защищенным наследством. давайте рассмотрим класс <code>base</code> и класс <code>child</code>, который наследуется от <code>base</code>.</p><ul> <li>если наследование <code>public</code>, все, кто знает о <code>base</code> и <code>child</code>, также знает, что <code>child</code> наследует от <code>base</code>.</li> <li>если наследование <code>protected</code>, только <code>child</code> и его дочерние элементы знают, что они наследуют от <code>base</code>.</li> <li>если наследование <code>private</code>, никто кроме <code>child</code> не знает о наследовании.</li> </ul>

<pre><code>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 }; </code></pre><p>важное примечание: все классы b, c и d содержат переменные x, y и z. это просто вопрос доступа.</p><p>об использовании защищенного и частного наследования вы можете прочитать <a href="https://stackoverflow.com/questions/374399/private-protected-inheritance/374423">здесь</a> .</p>

<p>ограничение видимости наследования сделает код неспособным увидеть, что какой-то класс наследует другой класс: неявные преобразования из производного в базовый не будут работать, и <code>static\_cast</code> из базового в производный также не будет работать.</p><p>только члены/друзья класса могут видеть личное наследование, и только члены/друзья и производные классы могут видеть защищенное наследование.</p><p><strong>публично</strong> наследование</p><ol> <li> <p>ис-а наследство. кнопка - это окно, и везде, где требуется окно, кнопка также может быть передана.</p> <pre><code>class button : public window { }; </code></pre></li> </ol><p><strong>защищено</strong> наследование</p><ol> <li> <p>защищено реализовано в сроках. редко полезно. используется в <code>boost::compressed\_pair</code> для извлечения из пустых классов и экономии памяти с помощью оптимизации пустого базового класса (в приведенном ниже примере шаблон не используется, чтобы оставаться в точке):</p> <pre><code>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! } }; </code></pre></li> </ol><p><strong>личное</strong> наследование</p><ol> <li> <p>реализована-в-ухудшений условий. использование базового класса только для реализации производного класса. полезно с чертами и если размер имеет значение (пустые черты, которые содержат только функции, будут использовать пустую оптимизацию базового класса). часто <em> сдерживание </em>, тем не менее, является лучшим решением. размер строк имеет решающее значение, поэтому здесь его часто используют.</p> <pre><code>template<typename storagemodel> struct string : private storagemodel { public: void realloc() { // uses inherited function storagemodel::realloc(); } }; </code></pre></li> </ol><hr><p><strong>общедоступный</strong> участник</p><ol> <li> <p>заполнитель</p> <pre><code>class pair { public: first first; second second; }; </code></pre></li> <li> <p>accessors</p> <pre><code>class window { public: int getwidth() const; }; </code></pre></li> </ol><p><strong>защищенный</strong> участник</p><ol> <li> <p>предоставление расширенного доступа для производных классов</p> <pre><code>class stack { protected: vector<element> c; }; class window { protected: void registerclass(window\_descriptor w); }; </code></pre></li> </ol><p><strong>личное</strong> участник</p><ol> <li> <p>сохранить детали реализации</p> <pre><code>class window { private: int width; }; </code></pre></li> </ol><hr><p>обратите внимание, что приведения в стиле c преднамеренно позволяют приводить производный класс к защищенному или частному базовому классу определенным и безопасным способом, а также приводить в другом направлении. этого следует избегать любой ценой, потому что это может сделать код зависимым от деталей реализации - но при необходимости вы можете использовать эту технику.</p>

<p>это связано с тем, как открытые члены базового класса выставляются из производного класса.</p><ul> <li>public -> публичные члены базового класса будут публичными (обычно по умолчанию)</li> <li>protected -> публичные члены базового класса будут защищены</li> <li>private -> публичные члены базового класса будут приватными</li> </ul><p>как указывает литб, публичное наследование - это традиционное наследование, которое вы увидите в большинстве языков программирования. то есть он моделирует отношения "is-a". частное наследование, то, что afaik свойственно c++, является отношением "реализовано в условиях". то есть вы хотите <strong>использовать</strong> открытый интерфейс в производном классе, но не хотите, чтобы пользователь производного класса имел доступ к этому интерфейсу. многие утверждают, что в этом случае вы должны агрегировать базовый класс, то есть вместо того, чтобы базовый класс был закрытым, сделать членом члена производного, чтобы повторно использовать функциональные возможности базового класса.</p>

<p>эти три ключевых слова также используются в совершенно другом контексте для указания <strong>модель наследования видимости</strong>.</p><p>в этой таблице собраны все возможные комбинации объявления компонента и модели наследования, представляющие итоговый доступ к компонентам, когда подкласс полностью определен.</p><p><img src="https://i.stack.imgur.com/w6cj3.jpg" alt="enter image description here"></p><p>таблица выше интерпретируется следующим образом (взгляните на первый ряд):</p><blockquote> <p><em> если компонент <strong>объявлен</strong> как <strong>открытый</strong> и его класс <strong>унаследован</strong> как <strong>открытый</strong> = результирующий <strong>доступ</strong> является <strong>общедоступным</strong>. </em></p> </blockquote><p>пример:</p><pre class="lang-cpp prettyprint-override"><code> class super { public: int p; private: int q; protected: int r; }; class sub : private super {}; class subsub : public sub {}; </code></pre><p>полученный доступ к переменным <code>p</code>, <code>q</code>, <code>r</code> в классе <strong>subsub</strong> равен <strong>нет</strong>.</p><blockquote> <p>другой пример:</p> </blockquote><pre class="lang-cpp prettyprint-override"><code>class super { private: int x; protected: int y; public: int z; }; class sub : protected super {}; </code></pre><p>результирующий доступ для переменных <code>y</code>, <code>z</code> в классе <strong>sub</strong> равен <strong>защищен</strong>, а для переменной <code>x</code> равен <strong>нет</strong>.</p><blockquote> <p>более подробный пример:</p> </blockquote><pre class="lang-cpp prettyprint-override"><code>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; } </code></pre><p>теперь давайте определим подкласс:</p><pre class="lang-cpp prettyprint-override"><code>class sub : super { }; int main(void) { sub object; object.put(100); object.put(object.get()); cout << object.get() << endl; return 0; } </code></pre><p>определенный класс с именем sub, который является подклассом класса с именем <code>super</code> или этот класс <code>sub</code> является производным от класса <code>super</code>. класс <code>sub</code> не вводит ни новых переменных, ни новых функций. означает ли это, что какой-либо объект класса <code>sub</code> наследует все черты после того, как класс <code>super</code> фактически является копией объектов класса <code>super</code>?</p><p><strong>нет</strong>. это не так.</p><p>если мы скомпилируем следующий код, мы получим только ошибки компиляции, говорящие о том, что методы <code>put</code> и <code>get</code> недоступны. зачем?</p><p>когда мы опускаем спецификатор видимости, компилятор предполагает, что мы собираемся применить так называемое <strong>личное наследование</strong>. это означает, что все компоненты <strong>public</strong> суперкласса превращаются в <strong>private</strong>, частные компоненты суперкласса вообще не будут доступны. следовательно, это означает, что вы не можете использовать последний внутри подкласса.</p><p>мы должны сообщить компилятору, что мы хотим сохранить ранее использованную политику доступа.</p><pre class="lang-cpp prettyprint-override"><code>class sub : public super { }; </code></pre><blockquote> <p><em> не вводите в заблуждение </em>: это не означает, что частные компоненты класса super (например, переменная хранения) в некоторой степени превратятся в публичные. волшебный путь. <strong>private</strong> компоненты останутся <strong>private</strong>, <strong>public</strong> останутся <strong>public</strong>.</p> </blockquote><p>объекты класса <code>sub</code> могут выполнять "почти" те же действия, что и их старшие братья и сестры, созданные из класса <code>super</code>. <em> "почти" </em>, потому что факт, что он является подклассом, также означает, что <strong>класс потерял доступ к закрытым компонентам суперкласса</strong>. мы не можем написать функцию-член класса <code>sub</code>, которая могла бы напрямую манипулировать переменной хранения.</p><p>это очень серьезное ограничение. есть ли обходной путь?</p><p><strong>да</strong>.</p><p>третий уровень доступа называется <strong>защищен</strong>. ключевое слово protected означает, что компонент, помеченный им <strong>ведет себя как общедоступный, когда используется любым из подклассов, и выглядит как частный для остального мира</strong>. - <em> это верно только для публично унаследованных классов (например, суперкласса в нашем примере) </em> -</p><pre class="lang-cpp prettyprint-override"><code>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; } </code></pre><p>как вы видите в примере кода, мы добавили новую функциональность в класс <code>sub</code>, и она делает одну важную вещь: <strong>она обращается к переменной хранения из класса super</strong>.</p><p>это было бы невозможно, если бы переменная была объявлена как private. в области видимости основной функции переменная все равно остается скрытой, поэтому, если вы напишите что-то вроде:</p><pre class="lang-cpp prettyprint-override"><code>object.storage = 0; </code></pre><p>компилятор сообщит вам, что это <code>error: 'int super::storage' is protected</code>.</p><p>наконец, последняя программа выдаст следующий вывод:</p><pre class="lang-cpp prettyprint-override"><code>storage = 101 </code></pre>

<pre><code>member in base class : private protected public </code></pre><p><strong>тип наследования</strong>: <strong>объект, унаследованный как</strong>:</p><pre><code>private : inaccessible private private protected : inaccessible protected protected public : inaccessible protected public </code></pre>

<p><strong>1) общественное наследование</strong>:</p><p>а. частные члены базового класса не доступны в производном классе.</p><p>б. защищенные члены базового класса остаются защищенными в производном классе.</p><p>с. открытые члены базового класса остаются публичными в производном классе.</p><p>итак, другие классы могут использовать открытые члены класса base через объект класса derived.</p><p><strong>2) защищенное наследование</strong>:</p><p>а. частные члены базового класса не доступны в производном классе.</p><p>б. защищенные члены базового класса остаются защищенными в производном классе.</p><p>с. открытые члены базового класса тоже становятся защищенными членами производного класса.</p><p>таким образом, другие классы не могут использовать публичные члены класса base через объект класса derived; но они доступны для подкласса derived.</p><p><strong>) частное наследство</strong>:</p><p>а. частные члены базового класса не доступны в производном классе.</p><p>б. защищенные и публичные члены базового класса становятся частными членами производного класса.</p><p>таким образом, никакие члены базового класса не могут быть доступны другим классам через объект класса derived, поскольку они являются частными в классе derived. таким образом, даже подкласс класса derived не может получить к ним доступ.</p>

<p>публичное наследование моделирует отношения is-a. с</p><pre><code>class b {}; class d : public b {}; </code></pre><p>каждый <code>d</code> <em>является</em> <code>b</code>.</p><p>частное наследование моделирует отношения is-implemented-using (или как там это называется). с</p><pre><code>class b {}; class d : private b {}; </code></pre><p><code>d</code> - это <em>не</em> <code>b</code>, но каждое <code>d</code> использует свое <code>b</code> в своей реализации. частное наследование всегда можно устранить, используя вместо этого сдерживание:</p><pre><code>class b {}; class d { private: b b\_; }; </code></pre><p>это <code>d</code> также может быть реализовано с использованием <code>b</code>, в данном случае с использованием его <code>b\_</code>. сдерживание - это менее тесная связь между типами, чем наследование, поэтому в целом оно должно быть предпочтительным. иногда использование сдерживания вместо частного наследования не так удобно, как частное наследование. часто это неуместное оправдание лени.</p><p>я не думаю, что кто-нибудь знает, что <code>protected</code> модели наследования. по крайней мере, я еще не видел убедительного объяснения.</p>

<p>если вы публично наследуете от другого класса, все знают, что вы наследуете, и вы можете полиморфно использоваться кем угодно через указатель базового класса.</p><p>если вы наследуете защищенно, только ваши дети смогут использовать вас полиморфно.</p><p>если вы наследуете конфиденциально, только вы сами сможете выполнять методы родительского класса.</p><p>что в основном символизирует знания остальных классов о ваших отношениях с родительским классом</p>

<p>к защищенным членам данных могут обращаться любые классы, которые наследуются от вашего класса. частные данные членов, однако, не могут. допустим, у нас есть следующее:</p><pre><code>class myclass { private: int myprivatemember; // lol protected: int myprotectedmember; }; </code></pre><p>внутри вашего расширения к этому классу ссылка на <code>this.myprivatemember</code> не будет работать. тем не менее, <code>this.myprotectedmember</code> будет. значение по-прежнему инкапсулировано, поэтому, если у нас есть экземпляр этого класса с именем <code>myobj</code>, то <code>myobj.myprotectedmember</code> не будет работать, поэтому он похож по функции на частный член данных.</p>

<pre><code>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 </code></pre><p>на основании <a href="https://stackoverflow.com/a/215505/1519058">этого</a> примера для java ... я думаю, что столик стоит тысячу слов :)</p>

<p>резюме:</p><ul> <li>частный: никто не может видеть это, кроме как в классе</li> <li>защищено: частные + производные классы могут видеть это</li> <li>общественность: мир может это увидеть</li> </ul><p>при наследовании вы можете (на некоторых языках) изменить тип защиты элемента данных в определенном направлении, например, от охраняемой до публичной.</p>

<h2>частный:</h2><p>закрытые члены базового класса могут быть доступны только членам этого базового класса.</p><h2>общественность:</h2><p>к открытым членам базового класса могут обращаться члены этого базового класса, члены его производного класса, а также члены, которые находятся за пределами базового класса и производного класса.</p><h2>защищено:</h2><p>защищенные члены базового класса могут быть доступны как членам базового класса, так и членам его производного класса.</p><hr><h2>короче:</h2><p><strong>личное</strong>: база</p><p><strong>защищено</strong>: база + производная</p><p><strong>общедоступный</strong>: базовый + производный + любой другой участник</p>

<p>я нашел простой ответ и подумал о том, чтобы опубликовать его и для дальнейшего использования.</p><p>это из ссылок <a href="http://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/">http://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/</a> </p><pre><code>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 } </code></pre>

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