Чи повинні ви коли-небудь використовувати захищені змінні-члени?


97

Чи повинні ви коли-небудь використовувати захищені змінні-члени? Які переваги та які проблеми це може спричинити?

Відповіді:


73

Чи повинні ви коли-небудь використовувати захищені змінні-члени?

Залежить від того, наскільки ви вимогливі до приховування стану.

  • Якщо ви не хочете будь-якого витоку внутрішнього стану, тоді оголошення всіх ваших змінних-членів приватними - це шлях.
  • Якщо вам насправді все одно, що підкласи можуть отримати доступ до внутрішнього стану, тоді захищений досить добре.

Якщо розробник приходить і підкласує ваш клас, вони можуть зіпсувати його, оскільки вони цього не розуміють повністю. З приватними членами, крім загальнодоступного інтерфейсу, вони не можуть бачити конкретні деталі реалізації того, як все робиться, що надає вам гнучкість його подальшої зміни.


1
Чи можете ви прокоментувати роботу захищених змінних проти приватної змінної методом get / set?
Джейк,

3
Я б сказав, що це не те, про що варто турбуватися, якщо ви не виявите за допомогою профілювання, що горлечко пляшки в кінцевому підсумку стає аксесуарами (а це майже ніколи не є). Є трюки, які можна зробити, щоб зробити JIT розумнішим щодо речей, якщо це виявиться проблемою. Наприклад, у Java ви можете натякнути, що аксесуар можна вбудувати, позначивши його як остаточний. Хоча чесно кажучи, продуктивність геттерів та установників набагато менш важлива, ніж робота з організацією системи та з реальними проблемами продуктивності, визначеними профілі.
Allain Lalonde

23
@Jake: Ніколи не слід приймати дизайнерські рішення на основі припущень про ефективність. Ви приймаєте дизайнерські рішення, виходячи з того, що, на вашу думку, є найкращим дизайном, і лише якщо у вашому реальному житті з’являється вузьке місце у вашому дизайні, ви йдете і виправляєте це. Зазвичай, якщо дизайн добрий, продуктивність теж хороша.
Мекі

З приватними членами, крім загальнодоступного інтерфейсу, вони не бачать деталей реалізації. Вони можуть просто відкрити клас і переглянути його, так що це не має сенсу ?!
Чорний

2
@Black Очевидно, Аллайн мав на увазі "вони не можуть отримати доступ " до цих учасників і тому не може будувати код проти них, залишаючи автору класу вільне вилучення / зміну захищених членів пізніше. (Звичайно, ідіома pimpl дозволила б приховувати їх візуально та від одиниць перекладу, включаючи заголовок, теж.)
underscore_d

31

Загальне відчуття сьогодні полягає в тому, що вони спричиняють надмірне зв’язування між похідними класами та їх базами.

Вони не мають особливих переваг над захищеними методами / властивостями (колись вони могли мати незначну перевагу в продуктивності), і вони також використовувались частіше в епоху, коли в моді було дуже глибоке успадкування, чого на даний момент немає.


2
Не повинно no particular advantage over protected methods/propertiesбути no particular advantage over *private* methods/properties?
Penghe Geng

Ні, оскільки я / говорив про переваги / недоліки різних способів спілкування між похідними класами та їх базами - усі ці методи будуть «захищені» - різниця полягає в тому, чи є вони змінними-членами (полями) або властивостями / методами ( тобто якісь підпрограми).
Will Dean

1
Дякуємо за швидке роз'яснення. Я радий, що за годину на моє запитання до 6-річного допису відповів оригінальний плакат. Ви не думаєте, що це може статися на більшості інших форумів в Інтернеті :)
Penghe Geng

9
Ще дивовижнішим є те, що я насправді погоджуюсь із самим собою стільки часу ...
Уілл Дін

Одним порядком бізнесу конструктора є забезпечення того, щоб усі змінні стану явно ініціалізувались. Якщо ви дотримуєтесь цієї угоди, ви можете використовувати superконструкцію для виклику батьківського конструктора; тоді він подбає про ініціалізацію змінних приватного стану в батьківському класі.
ncmathsadist

31

Як правило, якщо щось свідомо не сприймається як публічне, я роблю це приватним.

Якщо виникає ситуація, коли мені потрібен доступ до цієї приватної змінної або методу з похідного класу, я змінюю їх з приватного на захищений.

Це майже ніколи не трапляється - я насправді не шанувальник спадщини, оскільки це не особливо хороший спосіб моделювати більшість ситуацій. У будь-якому випадку, продовжуйте, не хвилюйтеся.

Я б сказав, що це нормально (і, мабуть, найкращий спосіб це зробити) для більшості розробників.

Справа в тому , що якщо через рік з’явиться якийсь інший розробник і вирішить, що їм потрібен доступ до вашої змінної приватного учасника, вони просто відредагують код, змінять його на захищений і продовжать свою справу.

Єдині реальні винятки з цього полягають у тому, що ви займаєтеся доставкою двійкових dll-файлів у формі чорної скриньки третім сторонам. Це, в основному, Microsoft, ті постачальники "Custom DataGrid Control" і, можливо, кілька інших великих програм, що постачаються з бібліотеками розширення. Якщо ви не входите до цієї категорії, не варто витрачати час / зусилля, щоб турбуватися про подібні речі.


8

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

Наприклад, якщо у мене є клас, який визначає ширину та висоту об'єкта, що відображається, і я роблю ці змінні захищеними, тоді я не можу робити жодних припущень щодо (наприклад) співвідношення сторін.

Критично важливо, що я ніколи не можу зробити ці припущення в будь-який момент з моменту випуску коду як бібліотеки, оскільки, навіть якщо я оновлю мої сетери, щоб підтримувати співвідношення сторін, я не маю гарантії того, що змінні встановлюються через сеттери або доступ до них через геттери в існуючому коді.

Також жоден підклас мого класу не може зробити цю гарантію, оскільки вони також не можуть застосовувати значення змінних, навіть якщо це вся суть їх підкласу .

Як приклад:

  • У мене є клас прямокутника з шириною та висотою, який зберігається як захищені змінні.
  • Очевидним підкласом (у моєму контексті) є клас "DisplayedRectangle", де єдина різниця полягає в тому, що я обмежую ширину та висоту дійсними значеннями для графічного відображення.
  • Але це зараз неможливо , оскільки мій клас DisplayedRectangle не може по-справжньому обмежити ці значення, оскільки будь-який його підклас може перевизначити значення безпосередньо, в той же час розглядаючись як DisplayedRectangle.

Обмежуючи змінні як приватні, я можу тоді застосувати поведінку, яку я хочу, через сетери або геттери.


7

Взагалі, я б зберіг ваші змінні захищеного члена до того рідкісного випадку, коли ви повністю контролюєте код, який їх також використовує. Якщо ви створюєте загальнодоступний API, я б сказав ніколи. Нижче ми будемо називати змінну-член як "властивість" об'єкта.

Ось що ваш суперклас не може зробити, зробивши змінну-член захищеною, а не приватною-з-accessors:

  1. ліниво створюйте значення на льоту, коли властивість читається. Якщо ви додаєте захищений метод отримання, ви можете ліниво створити значення і передати його назад.

  2. знати, коли властивість було змінено або видалено. Це може спричинити помилки, коли суперклас робить припущення про стан цієї змінної. Створення захищеного методу встановлення для змінної зберігає цей контроль.

  3. Встановіть точку зупинки або додайте вивід налагодження, коли змінна читається або в неї записується.

  4. Перейменуйте цю змінну-учасника, не переглядаючи весь код, який може її використовувати.

Загалом, я думаю, що це був би рідкісний випадок, коли я рекомендував би зробити змінну захищеного члена. Вам краще витратити кілька хвилин на викриття властивості за допомогою геттерів / сетерів, ніж через години, відстежуючи помилку в якомусь іншому коді, який змінив захищену змінну. Мало того, але ви застраховані від додавання майбутніх функціональних можливостей (таких як ледаче завантаження), не порушуючи залежний код. Це важче зробити пізніше, ніж зараз.


7

На рівні проектування може бути доречним використовувати захищене властивість, але для реалізації я не бачу переваг у відображенні цього на захищену змінну члена, а не на методи доступу / мутатора.

Захищені змінні-члени мають суттєві недоліки, оскільки вони ефективно дозволяють клієнтському коду (підкласу) отримувати доступ до внутрішнього стану класу базового класу. Це заважає базовому класу ефективно підтримувати свої інваріанти.

З тієї ж причини захищені змінні-члени також значно ускладнюють написання безпечного багатопотокового коду, якщо не гарантують константу або обмежуються одним потоком.

Методи доступу / мутатора пропонують значно більшу стабільність API та гнучкість реалізації при технічному обслуговуванні.

Крім того, якщо ви пурист ОО, об'єкти співпрацюють / спілкуються, надсилаючи повідомлення, а не читаючи / встановлюючи стан.

Натомість вони пропонують дуже мало переваг. Я б не обов'язково видаляв їх із чужого коду, але сам я їх не використовую.


4

Здебільшого небезпечно використовувати захищений, оскільки ви дещо порушуєте інкапсуляцію вашого класу, який цілком може бути розбитий погано розробленим похідним класом.

Але у мене є один хороший приклад: скажімо, ви можете зробити якийсь загальний контейнер. Він має внутрішню реалізацію та внутрішні аксесуари. Але вам потрібно запропонувати принаймні 3 загальнодоступних доступу до його даних: map, hash_map, vector-like. Тоді у вас є щось на зразок:

template <typename T, typename TContainer>
class Base
{
   // etc.
   protected
   TContainer container ;
}

template <typename Key, typename T>
class DerivedMap     : public Base<T, std::map<Key, T> >      { /* etc. */ }

template <typename Key, typename T>
class DerivedHashMap : public Base<T, std::hash_map<Key, T> > { /* etc. */ }

template <typename T>
class DerivedVector  : public Base<T, std::vector<T> >        { /* etc. */ }

Я використовував цей тип коду менше місяця тому (тому код з пам'яті). Поміркувавши, я вважаю, що хоча загальний контейнер Base повинен бути абстрактним класом, навіть якщо він може жити досить добре, оскільки використання безпосередньо Base було б такою болем, це слід забороняти.

Підсумок Таким чином, ви захистили дані, що використовуються похідним класом. Тим не менше, ми повинні враховувати той факт, що базовий клас повинен бути абстрактним.


це не порушує інкапсуляцію більше, ніж це роблять представники громадськості. Це налаштування сказати, що похідні класи можуть використовувати стан класу, який не піддається користувачам класу.
gbjbaanb

@gbjbaanb: Ви суперечите собі "це не порушує інкапсуляцію більше, ніж це роблять загальнодоступні", і відрізняється від "[лише] похідні класи можуть використовувати клас 'state'. "захищений" - це середина між державним і приватним. Тож "захищений [...] дещо порушує інкапсуляцію" все ще правда ...
paercebal

насправді, в мові c ++, адаптери контейнерів, такі як std :: stack, виставлять базовий об'єкт контейнера із захищеною змінною під назвою "c".
Йоханнес Шауб - літб

Я знаю, що ця публікація досить стара, але я відчуваю необхідність натякати. Ви не "дещо" порушуєте інкапсуляцію, ви повністю її порушуєте. protectedне більше інкапсульований, ніж public. Я готовий, щоб довели, що я неправий. Все, що вам потрібно зробити, це написати клас із захищеним членом і заборонити мені змінювати його. Очевидно, що клас повинен бути не остаточним, оскільки вся суть використання захищеного - для успадкування. Або щось інкапсульовано, або ні. Не існує проміжного стану.
Taekahn

3

Коротше, так.

Захищені змінні-члени дозволяють доступ до змінної з будь-яких підкласів, а також будь-яких класів того самого пакета. Це може бути дуже корисним, особливо для даних лише для читання. Однак я не вірю, що вони коли-небудь необхідні, тому що будь-яке використання захищеної змінної члена може бути відтворено за допомогою приватної змінної члена та декількох геттерів та сетерів.


1
І навпаки, змінні приватного члена також ніколи не потрібні; публічного достатньо для будь-якого використання.
Аліса

3

Тільки для запису, під пунктом 24 "Винятковий C ++", в одній із виносок Саттер йде "ви ніколи не писали б клас, який має загальнодоступну або захищену змінну члена. Так? (Незалежно від поганого прикладу, наведеного деякими бібліотеками .) "


2

Для отримання детальної інформації про модифікатори доступу .Net перейдіть сюди

У захищених змінних-членів немає реальних переваг чи недоліків, це питання того, що вам потрібно у вашій конкретній ситуації. Загалом прийнято практику оголошувати змінні-члени приватними та вмикати зовнішній доступ через властивості. Крім того, деякі інструменти (наприклад, деякі O / R карти) очікують, що дані об'єкта будуть представлені властивостями і не розпізнають загальнодоступні або захищені змінні члена. Але якщо ви знаєте, що хочете, щоб ваші підкласи (і ТІЛЬКИ ваші підкласи) отримували доступ до певної змінної, немає причин не оголошувати її захищеною.


Бажання підкласів отримати доступ до змінної дуже відрізняється від бажання мати можливість вільно її мутувати . Це один з головних аргументів проти захищених змінних: тепер ваш базовий клас не може приймати жоден з його інваріантів, оскільки будь-який похідний клас може робити абсолютно все із захищеними членами. Це головний аргумент проти них. Якщо їм просто потрібен доступ до даних, тоді ... напишіть аксесуар. : P (Я використовую захищені змінні, хоча, мабуть, більше, ніж мав би, і буду намагатись скоротити!)
underscore_d
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.