В чем разница между наследованием
public
,private
иprotected
в C ++?
Все вопросы, которые я нашел по SO, относятся к конкретным случаям.
В чем разница между наследованием
public
,private
иprotected
в C ++?
Все вопросы, которые я нашел по SO, относятся к конкретным случаям.
Чтобы ответить на этот вопрос, я хотел бы сначала описать аксессоров участников своими словами. Если вам это уже известно, перейдите к заголовку «следующий:».
Мне известны три средства доступа: 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
, не знает о наследовании.SomeBase
похоже на жестко запрограммированный способ создания анонимного члена типа SomeBase
. Этот, как и любой другой член, имеет спецификатор доступа, который оказывает такое же управление внешним доступом.
- person underscore_d; 27.02.2016
private
. protected
объявления базового класса доступны для дочернего объекта, а не для class: struct Base { protected: int x; }; struct Desc : Base { Desc(Base& b) { (void)b.x; } };
не компилируется.
- person lorro; 08.04.2017
Derived::SomeBase
- person curiousguy; 29.11.2018
b1
и b2
одного и того же класса Base
, тогда как b1
получить доступ (а также изменить) частные члены b2
?
- person Milan; 21.08.2020
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. Это просто вопрос доступа.
Об использовании защищенного и частного наследования вы можете прочитать здесь.
Ограничение видимости наследования приведет к тому, что код не сможет увидеть, что какой-то класс наследует другой класс: неявные преобразования из производного в базовый не будут работать, и static_cast
из базового в производное тоже не будет работать.
Только члены / друзья класса могут видеть частное наследование, и только члены / друзья и производные классы могут видеть защищенное наследование.
публичное наследование
IS-Наследование. Кнопка - это окно, и везде, где необходимо окно, кнопку тоже можно передать.
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;
};
Аксессоры
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 специально позволяет преобразовывать производный класс в защищенный или частный базовый класс определенным и безопасным образом, а также выполнять приведение в другом направлении. Этого следует избегать любой ценой, потому что это может сделать код зависимым от деталей реализации, но при необходимости вы можете использовать эту технику.
Эти три ключевых слова также используются в совершенно другом контексте для определения модели наследования видимости.
В этой таблице собраны все возможные комбинации объявления компонента и модели наследования, представляющие результирующий доступ к компонентам, когда подкласс полностью определен.
Приведенная выше таблица интерпретируется следующим образом (взгляните на первую строку):
если компонент объявлен как общедоступный, а его класс унаследован как общедоступный, результирующий доступ является общедоступным.
Пример:
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 { };
Не обманывайтесь: это не означает, что частные компоненты суперкласса (например, переменная хранилища) каким-то волшебным образом превратятся в общедоступные. Частные компоненты останутся частными, общедоступными останутся общедоступными.
Объекты класса 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
Это связано с тем, как открытые члены базового класса предоставляются из производного класса.
Как указывает литб, публичное наследование - это традиционное наследование, которое вы увидите в большинстве языков программирования. То есть моделирует отношения "IS-A". Частное наследование, что-то, что, как мне кажется, свойственно C ++, является отношением «РЕАЛИЗОВАНО В УСЛОВИЯХ». То есть вы хотите использовать открытый интерфейс в производном классе, но не хотите, чтобы пользователь производного класса имел доступ к этому интерфейсу. Многие утверждают, что в этом случае вы должны агрегировать базовый класс, то есть вместо того, чтобы иметь базовый класс в качестве частной базы, сделать член производного, чтобы повторно использовать функциональность базового класса.
Member in base class : Private Protected Public
Тип наследования: Объект, унаследованный как:
Private : Inaccessible Private Private
Protected : Inaccessible Protected Protected
Public : Inaccessible Protected Public
1) Общедоступное наследование:
а. Закрытые члены базового класса недоступны в производном классе.
б. Защищенные члены базового класса остаются защищенными в производном классе.
c. Открытые члены базового класса остаются открытыми в производном классе.
Таким образом, другие классы могут использовать общедоступные члены базового класса через объект производного класса.
2) Защищенное наследование:
а. Закрытые члены базового класса недоступны в производном классе.
б. Защищенные члены базового класса остаются защищенными в производном классе.
c. Открытые члены базового класса тоже становятся защищенными членами производного класса.
Таким образом, другие классы не могут использовать открытые члены базового класса через объект производного класса; но они доступны для подкласса Derived.
3) Частное наследование:
а. Закрытые члены базового класса недоступны в производном классе.
б. Защищенные и открытые члены базового класса становятся частными членами производного класса.
Таким образом, никакие члены базового класса не могут быть доступны другим классам через объект производного класса, поскольку они являются частными в производном классе. Таким образом, даже подкласс класса Derived не может получить к ним доступ.
Публичное наследование моделирует отношения IS-A. С участием
class B {};
class D : public B {};
каждый D
является B
.
Частное наследование моделирует отношения IS-IMPLEMENTED-USING (или как там это называется). С участием
class B {};
class D : private B {};
D
не B
, но каждый D
использует свой B
в своей реализации. Частное наследование всегда можно исключить, используя вместо него сдерживание:
class B {};
class D {
private:
B b_;
};
Этот D
тоже может быть реализован с помощью B
, в данном случае с его b_
. Сдерживание - это менее тесная связь между типами, чем наследование, поэтому в целом ему следует предпочесть. Иногда использование включения вместо частного наследования не так удобно, как частное наследование. Часто это неубедительное оправдание лени.
Я не думаю, что кто-то знает, какие protected
модели наследования. По крайней мере, убедительного объяснения я пока не видел.
D
является производным от D
, он может переопределить виртуальные функции B
. (Если, например, B
является интерфейсом наблюдателя, то D
может реализовать его и передать this
функциям, требующим какой-либо интерфейс, без возможности использовать D
в качестве наблюдателя.) Кроме того, D
может выборочно сделать элементы B
доступными в свой интерфейс, выполнив using B::member
. Оба варианта синтаксически неудобны для реализации, когда B
является членом.
- person sbi; 06.11.2015
protected
наследование, которое я нашел полезным с virtual
базовым классом и protected
ctor: struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
- person lorro; 08.04.2017
Если вы публично наследуете от другого класса, все знают, что вы наследуете, и вы можете полиморфно использовать кем угодно через указатель базового класса.
Если вы наследуете защищенно, только ваши дочерние классы смогут использовать вас полиморфно.
Если вы наследуете частным образом, только вы сможете выполнять методы родительского класса.
Что в основном символизирует знания остальных классов о ваших отношениях с родительским классом.
Доступ к защищенным элементам данных могут получить любые классы, унаследованные от вашего класса. Однако члены с частными данными не могут. Допустим, у нас есть следующее:
class MyClass {
private:
int myPrivateMember; // lol
protected:
int myProtectedMember;
};
Изнутри вашего расширения к этому классу ссылка на this.myPrivateMember
не будет работать. Однако this.myProtectedMember
будет. Значение по-прежнему инкапсулировано, поэтому, если у нас есть экземпляр этого класса с именем myObj
, то myObj.myProtectedMember
не будет работать, поэтому он аналогичен функции частного члена данных.
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 ... Я думаю, что столик стоит тысячи слов :)
Резюме:
При наследовании вы можете (на некоторых языках) изменить тип защиты элемента данных в определенном направлении, например от защищенного до публичного.
Доступ к закрытым членам базового класса могут получить только члены этого базового класса.
Открытые члены базового класса могут быть доступны членам этого базового класса, членам его производного класса, а также членам, которые находятся за пределами базового класса и производного класса.
К защищенным членам базового класса могут получить доступ члены базового класса, а также члены его производного класса.
частный: базовый
защищенный: базовый + производный
общедоступный: базовый + производный + любой другой член
Я попытался объяснить наследование, используя картинку ниже.
Основная суть заключается в том, что частные члены родительского класса никогда не доступны напрямую из производного / дочернего класса, но вы можете использовать функцию члена родительского класса для доступа к частным членам родительского класса. Частные переменные всегда присутствуют в производном классе, но не могут быть доступны производному классу. Это похоже на их, но вы не можете видеть своими глазами, но если вы спросите кого-нибудь из родительского класса, он сможет вам это описать.
Я нашел простой ответ и подумал о том, чтобы опубликовать его и для справки в будущем.
Его можно найти по ссылкам 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
}
По сути, это защита доступа публичных и защищенных членов базового класса в производном классе. При общедоступном наследовании производный класс может видеть общедоступные и защищенные члены базы. С частным наследованием этого не может. С protected производный класс и любые производные классы могут их видеть.