Чи хороша практика робити геттерів та сетерів вбудованими?


76
public:
     inline int GetValue() const {
          return m_nValue;
     }
     inline void SetValue(int nNewValue) {
          this -> m_nValue = nNewValue;
     }

На Learn C ++ вони сказали, що це буде працювати швидше. Отже, я подумав, що було б чудово використовувати на геттерах та сеттерах. Але, можливо, є деякі недоліки?


1
Дякую всім! Загальний висновок: Не робіть це, про це подбає компілятор.
Martijn Courteaux

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

4
Дозвольте додати кілька слів до відповіді, яку я дав нижче. Особисто мені не подобається захаращувати декларацію класу кодом, оскільки я вважаю це частиною (технічної) документації. Той самий аргумент для визначення методу у файлі заголовка, хоча і не такий поганий. Ну і нарешті: Вам справді потрібні геттери та сетери? :-)
mkluwe

2
@mkluwe +1 Я згоден, геттери та сетери рідко бувають частиною гарної практики.
daramarak

2
@daramarak: насправді, це переважно сетери, які є поганою практикою.
Андре Карон,

Відповіді:


66

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

Компілятор C ++ дуже розумний і майже напевно автоматично вбудовує таку просту функцію, як ця для вас. І, як правило, це розумніше, ніж ти, і буде робити набагато кращу роботу, визначаючи, що слід чи що не слід вкладати.

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


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

22
Фактично, як GCC, так і VS пропонують вбудовування між вихідними файлами.
Щеня,

7
@Mark - це наче правда, оскільки на цьому етапі це насправді компоновник, а не компілятор.
Едвард Стрендж

2
@DeadMG, я ніколи про це не чув і не уявляю, як це буде реалізовано. Отримали посилання?
Марк Ренсом

3
Можливо, компонувальник може виконати цю роботу, якщо обидва файли знаходяться в одному проекті. Але якщо ви зв’яжете попередньо скомпільовану DLL, вона не зможе вставити все, що явно не міститься у заголовних файлах.
мммммммм

30

Якщо ви пишете їх всередині визначення, вони вважаються inline за замовчуванням .

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


4
Якщо компілятор хоче вбудувати їх ...
mk12

5
Стандарт C ++ розглядає функції, inlineякщо вони визначені у визначенні класу, яке майже повністю не пов'язане з компілятором / компонувальником, що вкладає функцію.
Mooing Duck

15

Це погана практика у відкритих API. Будь-яка зміна цих функцій вимагає перекомпіляції всіх клієнтів.

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


6
Ви могли б детальніше розповісти про погану абстракцію? що саме ви маєте на увазі і як це відображається на цьому прикладі? чи m_nValue має бути просто публічним?
stijn

7
@stijn: Клас повинен бути абстракцією, ну, чогось. Його реалізація не повинна мати значення, а операції над нею повинні мати значення для того, що вона представляє. Повинні бути інваріанти класу: твердження, які завжди відповідають дійсності щодо класу. Геттери та сеттери виставляють реалізацію майже настільки ж, наскільки відкриті змінні. З мого досвіду, значення окремих змінних, як правило, не мають значення з точки зору абстракції класу, тому ви дозволяєте операції з класом, які не мають відношення до його абстракції. Геттери та сетери ускладнюють утримання інваріантів.
Девід Торнлі,

67
Гіпердогматична нісенітниця. Ви не думаєте, що потрібно отримувати довжину рядка? Змінити текст на кнопці інтерфейсу користувача? Отримати поточні координати миші? Звичайно, можна зловживати геттерами та сеттерами, як це стосується будь-якої моделі. Але "не робіть цього", оскільки загальне правило - це погана порада ІМО.
user168715

3
Я думаю, що у всіх є надмірно чутливий момент. Очевидно, що контейнери мають засоби доступу (ну, еквівалентні за допомогою посилань): це явно частина їхньої мети, щоб змінити певні модифікувані значення. Очевидно, що більшість класів не є контейнерами. Проблема з порадою "загалом, не роби цього" полягає в тому, що важко скористатися порадою - на будь-який приклад хорошого геттера / сеттера відповідь "Я сказав, що не роби цього загалом, а не" робіть це, коли це гарна ідея ". Тож порада точно еквівалентна: "загалом, робіть лише те, що є гарною ідеєю за конкретних обставин";
Стів Джессоп,

3
@ Стів: Можливо, ми можемо придумати щось корисніше. Ось моя пропозиція: Не проектуйте в геттерах і сеттерах. Дизайн функцій, корисних з точки зору абстракції. Має сенс, наприклад, говорити про координату X точки або довжину рядка. Якщо вони виявляються, по суті, геттерами та сеттерами, ну це просто реалізувати.
Девід Торнлі,

10

Негативні моменти:

  1. Компілятор може вільно ігнорувати вас.

  2. Будь-яка зміна цих функцій вимагає перекомпіляції всіх клієнтів.

  3. Хороший компілятор все одно вставить невбудовані функції, коли це доречно.


5

Я також хотів би додати, що якщо ви не виконуєте мільйони наборів / наборів за кадр, це майже не має значення, вбудовані вони чи ні. Чесно кажучи, не варто втрачати сон.

Крім того, майте на увазі, що те, що ви поставили слово "вбудований" перед декларацією + визначенням, не означає, що компілятор вбудує ваш код. Він використовує різні евристики, щоб з’ясувати, чи має це сенс, що часто є класичним компромісом швидкості проти розміру. Однак існує ключове слово "__forceinline" за наймом у VC ++ (я не впевнений, що це в GCC), яке накладає увагу на фантастичну евристику компіляторів. Я насправді не рекомендую це взагалі, і, крім того, як тільки ви перенесете іншу архітектуру, це, швидше за все, буде неправильним.

Спробуйте помістити всі визначення функцій у файл реалізації та залиште чисті декларації для заголовків (якщо, звичайно, ви не метапрограмуєте шаблон (STL / BOOST / і т. Д.), І в цьому випадку майже все є в заголовках;))

Одне з класичних місць, які люди люблять вбудовувати (принаймні у відеоіграх, звідки я родом), - це математичні заголовки. Продукти з перехрещенням / крапками, довжини векторів, очищення матриці тощо часто розміщуються в заголовку, що, на мою думку, є непотрібним. 9/10 це не має значення для продуктивності, і якщо вам коли-небудь потрібно буде зробити щільний цикл, наприклад, перетворення великого векторного масиву за допомогою якоїсь матриці, вам, мабуть, краще вручну робити математику вбудовано або навіть краще її кодувати в асемблер для певної платформи.

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

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

Удачі.

Шейн


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

3

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


2

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

До речі, ви можете пропустити, inlineякщо реалізуєте безпосередньо у визначенні класу.


2

Вбудоване ключове слово безглуздо у вашому випадку

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

Ключове слово inline впливає на посилання, а не на вбудовування. Це трохи заплутано, але прочитайте це.

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



1

Не потрібно, почніть довіряти компіляторам, принаймні для таких оптимізацій!
"Але не завжди"


1

Треба сказати, у мене немає сильного відрази до цієї практики, якої, здається, мають інші учасники цієї теми. Я погоджуюсь з тим, що приріст продуктивності від вставки є незначним у всіх випадках, крім найбільш широко використовуваних. (І так, я маю стикався з такими випадками на практиці.) Де я такий роду вбудовування, я роблю це для зручності, і як правило , тільки для дотепів , як це. У більшості випадків мого використання потреба уникати перекомпіляції на стороні клієнта, якщо я коли-небудь їх змінюю, просто не така сильна.

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

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

Візьміть це, пуристи. :-)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.