Разница между частным, публичным и защищенным наследованием
-
21-08-2019 - |
Вопрос
В чем разница между 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
осведомлен о наследовании.
Другие советы
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
переход от базового к производному тоже не сработает.
Только члены / друзья класса могут видеть частное наследование, и только члены / друзья и производные классы могут видеть защищенное наследование.
публичный наследование
ЭТО-наследство.Кнопка - это окно, и в любом месте, где требуется окно, кнопка тоже может быть передана.
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 намеренно позволяет приводить производный класс к защищенному или частному базовому классу определенным и безопасным способом, а также выполнять приведение в другом направлении.Этого следует избегать любой ценой, поскольку это может сделать код зависимым от деталей реализации, но при необходимости вы можете воспользоваться этим приемом.
Это связано с тем, как общедоступные члены базового класса предоставляются из производного класса.
- общедоступные -> общедоступные члены базового класса будут общедоступными (обычно по умолчанию)
- защищенный -> открытые члены базового класса будут защищены
- частные -> общедоступные члены базового класса будут закрытыми
Как указывает litb, публичное наследование - это традиционное наследование, которое вы увидите в большинстве языков программирования.То есть он моделирует отношения "ЕСТЬ-А".Частное наследование, что-то AFAIK, характерное для C ++, является отношением, "РЕАЛИЗОВАННЫМ В ТЕРМИНАХ".То есть вы хотите, чтобы использование общедоступный интерфейс в производном классе, но не хотите, чтобы пользователь производного класса имел доступ к этому интерфейсу.Многие утверждают, что в этом случае вы должны агрегировать базовый класс, то есть вместо того, чтобы использовать базовый класс в качестве частной базы, make в члене derived, чтобы повторно использовать функциональность базового класса.
Эти три ключевых слова также используются в совершенно другом контексте для указания модель наследования видимости.
В этой таблице собраны все возможные комбинации объявления компонента и модели наследования, представляющие результирующий доступ к компонентам, когда подкласс полностью определен.
Приведенная выше таблица интерпретируется следующим образом (взгляните на первую строку).:
если компонент является объявленный как публичный и его класс таков унаследованный как публичный результирующий доступ является публичный.
Пример:
class Super {
public: int p;
private: int q;
protected: int r;
};
class Sub : private Super {};
class Subsub : public Sub {};
Результирующий доступ к переменным p
, q
, r
в классе Подсубъект является Нет.
Другой пример:
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 (например, переменная storage) превратятся в общедоступные несколько волшебным образом. Частное компоненты останутся Частное, публичный останется публичный.
Объекты в 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
Member in base class : Private Protected Public
Тип наследования : Объект , унаследованный как:
Private : Inaccessible Private Private
Protected : Inaccessible Protected Protected
Public : Inaccessible Protected Public
1) Публичное Наследование:
a.Закрытые члены базового класса недоступны в производном классе.
b.Защищенные члены базового класса остаются защищенными в производном классе.
c.Открытые члены базового класса остаются открытыми в производном классе.
Таким образом, другие классы могут использовать общедоступные члены базового класса через производный class object.
2) Защищенное Наследование:
a.Закрытые члены базового класса недоступны в производном классе.
b.Защищенные члены базового класса остаются защищенными в производном классе.
c.Открытые члены базового класса тоже становятся защищенными членами производного класса.
Таким образом, другие классы не могут использовать общедоступные члены базового класса через объект производного класса;но они доступны для подкласса Derived.
3) Частное Наследование:
a.Закрытые члены базового класса недоступны в производном классе.
b.Защищенные и общедоступные члены базового класса становятся закрытыми членами производного класса.
Таким образом, никакие члены базового класса не могут быть доступны другим классам через объект производного класса, поскольку они являются закрытыми в производном классе.Таким образом, даже подкласс производного класс не может получить к ним доступ.
Публичное наследование моделирует отношение 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
модели наследования.По крайней мере, я пока не видел никакого убедительного объяснения.
Если вы публично наследуете от другого класса, все знают, что вы наследуете, и любой пользователь может использовать вас полиморфно через указатель базового класса.
Если вы наследуете защищенным образом, только ваши дочерние классы смогут использовать вас полиморфно.
Если вы наследуете конфиденциально, только вы сами сможете выполнять методы родительского класса.
Что в основном символизирует знания, которыми обладают остальные классы о ваших отношениях с вашим родительским классом
Защищенные элементы данных могут быть доступны любым классам, наследуемым от вашего класса.Однако участники с личными данными не могут этого сделать.Допустим, у нас есть следующее:
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 производный класс и любые производные от него классы могут их видеть.