Чи дає вам C # "менше мотузки, щоб повісити себе", ніж C ++? [зачинено]


14

Джоел Спольський охарактеризував С ++ як "достатню мотузку, щоб повісити себе" . Власне, він підсумовував "Ефективний C ++" Скотта Мейерса:

Це в основному книга, що в основному говорить, що на C ++ вистачає мотузки, щоб повісити себе, а потім ще пару зайвих миль мотузки, а потім ще дві таблетки для самогубства, замасковані під M & Ms ...

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

Ось мої запитання:

  1. Чи C # уникає підводних каменів, яких у C ++ уникає лише ретельне програмування? Якщо так, то в якій мірі і як їх уникнути?
  2. Чи є нові, різні підводні камені в C #, про які повинен знати новий програміст C #? Якщо так, то чому їх не вдалося уникнути дизайном C #?

10
З FAQ : Your questions should be reasonably scoped. If you can imagine an entire book that answers your question, you’re asking too much.. Я вважаю, що це є таким питанням ...
Одід

@Oded Ви маєте на увазі запитання з обмеженими символами? Або мої 3+ точніші запитання в тій частині моєї публікації?
alx9r

3
Чесно кажучи - і заголовок, і кожне з "точніших питань".
Одід

3
Я розпочав мета-дискусію з цього питання.
Одід

1
Що стосується вашого тепер видаленого третього запитання, то Ефективна серія C # Білла Вагнера (зараз 3 книги) навчила мене більше про програмування на C # добре, ніж усе, що я читав з цього приводу. Огляд Мартінса EC # має рацію в тому, що він ніколи не може бути прямою заміною для Ефективного C ++, але він помиляється, думаючи, що це має бути. Після того, як вам більше не доведеться турбуватися про легкі помилки, вам доведеться перейти до більш твердих помилок.
Марк Бут

Відповіді:


33

Принципова різниця між C ++ і C # випливає з невизначеної поведінки .

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

C / C ++:

У C ++, коли ви робите помилку, результат не визначений.
Або, якщо ви спробуєте зробити певні види припущень щодо системи (наприклад, підписане ціле число переповнення), велика ймовірність, що ваша програма буде невизначена.

Можливо, прочитайте цю 3-частину серію про невизначену поведінку.

Це робить C ++ настільки швидким - компілятору не потрібно турбуватися про те, що станеться, коли все піде не так, тому він може уникнути перевірки правильності.

C #, Java тощо.

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

Все інше - просто підлив.


Усі невизначені речі дійсно визначені реалізацією, тому якщо ви переймете непідписане ціле число у Visual Studio, ви отримаєте виняток, якщо ви ввімкнули правильні прапорці компілятора. Тепер я знаю, що це те, про що ви говорите, але це не визначена поведінка , це просто те, що люди зазвичай не перевіряють цього. (те саме з дійсно невизначеною поведінкою, як оператор ++, це добре визначає кожен компілятор). Ви можете сказати те саме з C #, тільки є лише 1 реалізація - безліч "невизначених поведінок", якщо ви працюєте в Mono - наприклад. bugzilla.xamarin.com/show_bug.cgi?id=310
gbjbaanb

1
Це дійсно визначено чи визначається лише тим, що робить поточна реалізація .net у поточній версії Windows? Навіть невизначена поведінка c ++ повністю визначається, якщо ви визначаєте її як усе, що робить g ++.
Мартін Бекетт

6
Переповнення непідписаних цілих чисел зовсім не є UB. Це переповнені підписані цілі числа, це UB.
DeadMG

6
@gbjbaanb: Як сказав DeadMG - переливання підписаного цілого числа не визначено. Це не визначено реалізацією. Ці фрази мають специфічне значення в стандарті C ++, і це не одне і те ж. Не робіть цієї помилки.
користувач541686

1
@CharlesSalvia: Так, як саме "C ++ полегшує використання кешу CPU", ніж C #? І який контроль дає C ++ над пам’яттю, якої ви не можете мати у C #?
користувач541686

12

Чи C # уникає підводних каменів, яких у C ++ уникає лише ретельне програмування? Якщо так, то в якій мірі і як їх уникнути?

Більшість це робить, деякі - ні. І звичайно, це робить деякі нові.

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

  2. Витоки пам’яті - це менше турбує сучасний C ++, але для початківців і протягом приблизно половини його життя C ++ зробив надзвичайно легким протікання пам’яті. Ефективний C ++ виявився прямо навколо еволюції практик для усунення цієї проблеми. Однак, C # все ще може просочити пам'ять. Найпоширеніший випадок, з яким стикаються люди - це захоплення подій. Якщо у вас є об'єкт і застосуйте один із його методів як обробник події, власником цієї події потрібно бути GC'd, щоб об’єкт помер. Більшість початківців не розуміють, що обробник подій вважається еталоном. Також є проблеми з нерозпорядженням одноразових ресурсів, які можуть просочити пам'ять, але вони не є настільки поширеними, як покажчики в попередньо ефективному C ++.

  3. Компіляція - C ++ має відсталу модель компіляції. Це призводить до низки хитрощів, щоб грати з ним добре і тримати компіляцію.

  4. Струни - Сучасний C ++ робить це трохи кращим, але char*відповідає за 95% усіх порушень безпеки до 2000 року. Для досвідчених програмістів вони зосереджуватимуться увагу std::string, але все-таки варто уникати проблеми в старих / гірших бібліотеках . І це моляться, що вам не потрібна підтримка unicode.

І справді, це вершина айсберга. Основне питання полягає в тому, що C ++ є дуже поганою мовою для початківців. Це досить непослідовно, і багато старих справді, справді поганих підводних каменів були вирішені шляхом зміни ідіом. Проблема полягає в тому, що початківцям тоді потрібно вивчити ідіоми з чогось на зразок Ефективного С ++. C # цілком усуває багато цих проблем і не викликає занепокоєння решти, поки ви не просунетесь далі на шляху навчання.

Чи є нові, різні підводні камені в C #, про які повинен знати новий програміст C #? Якщо так, то чому їх не вдалося уникнути дизайном C #?

Я згадав про проблему "Витік пам'яті". Це не мовна проблема настільки, як програміст очікував чогось, що мова не може зробити.

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

Ще одна напівпроблема, на яку я бачив програмістів, - це семантика захоплення анонімних функцій. Коли ви захоплюєте змінну, ви захоплюєте змінну . Приклад:

List<Action> actions = new List<Action>();
for(int x = 0; x < 10; ++x ){
    actions.Add(() => Console.WriteLine(x));
}

foreach(var action in actions){
    action();
}

Не робить те, що наївно вважається. Це друкується 1010 разів.

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


4
Витоки пам’яті - це минуле, і так є char*. Не кажучи вже про те, що ви все одно можете просочити пам'ять у C # просто чудово.
DeadMG

2
Виклик шаблонів "прославлене вставлення рядків" - це небагато. Шаблони справді є однією з найкращих особливостей C ++.
Чарльз Сальвія

2
@CharlesSalvia Звичайно, вони дійсно відмінна риса C ++. І так, це, можливо, надмірне спрощення впливу на компіляцію. Але вони непропорційно впливають на час компіляції та розмір виводу, особливо якщо ви не обережні.
Теластин

2
@deadMG, звичайно, хоча б я стверджував, що багато хитрощів метапрограмування шаблонів, які використовуються / потрібні в C ++, ... краще реалізуються за допомогою іншого механізму.
Теластин

2
@Telastyn вся суть type_traits полягає в тому, щоб отримати інформацію про тип під час компіляції, щоб ви могли використовувати цю інформацію, щоб виконувати такі дії, як спеціалізація шаблонів або перевантаження функцій певними способами, використовуючиenable_if
Чарльз Сальвія,

10

На мою думку, небезпека С ++ дещо перебільшена.

Основна небезпека полягає в наступному: хоча C # дозволяє виконувати "небезпечні" операції вказівника за допомогою unsafeключового слова, C ++ (в основному це набір C) дозволить вам використовувати покажчики, коли вам це заманеться. Окрім звичних небезпек, пов’язаних із використанням покажчиків (які однакові із C), як-от витоку пам'яті, переповнення буфера, вивішування покажчиків тощо, C ++ пропонує вам нові способи серйозно викрутити речі.

Ця "додаткова мотузка", про яку говорив Йоел Спольський , в основному зводиться до одного: написання класів, які внутрішньо управляють власною пам'яттю, також відоме як " Правило 3 " (яке тепер можна назвати Правилом 4 або Правило 5 у З ++ 11). Це означає, що якщо ви хочете коли-небудь написати клас, який керує власними розподілами пам'яті внутрішньо, ви повинні знати, що ви робите, інакше ваша програма, швидше за все, вийде з ладу. Ви повинні ретельно створити конструктор, копіювати конструктор, деструктор та оператор присвоєння, що напрочуд легко помилитися, що часто призводить до химерних збоїв під час виконання.

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

class Foo
{
    public:

    Foo(const std::string& s) 
        : m_first_name(s)
    { }

    private:

    std::string m_first_name;
};

Цей клас схожий на те, що ви робите в Java або C # - він не вимагає явного керування пам’яттю (оскільки бібліотечний клас std::stringдбає про все, що автоматично), і ніяких матеріалів «Правила 3» взагалі не потрібно, оскільки за замовчуванням конструктор копій та оператор присвоєння добре.

Це лише коли ви намагаєтесь зробити щось на кшталт:

class Foo
{
    public:

    Foo(const char* s)
    { 
        std::size_t len = std::strlen(s);
        m_name = new char[len + 1];
        std::strcpy(m_name, s);
    }

    Foo(const Foo& f); // must implement proper copy constructor

    Foo& operator = (const Foo& f); // must implement proper assignment operator

    ~Foo(); // must free resource in destructor

    private:

    char* m_name;
};

У цьому випадку новачкам може бути складно виправити завдання, деструктор та конструктор копій правильно. Але для більшості випадків немає причин ніколи цього робити. C ++ дозволяє дуже легко уникнути ручного управління пам’яттю в 99% часу, використовуючи бібліотечні класи типу std::stringі std::vector.

Інша пов’язана проблема - це керування пам’яттю вручну таким чином, що не враховує можливість викидання виключень. Подобається:

char* s = new char[100];
some_function_which_may_throw();
/* ... */
delete[] s;

Якщо на some_function_which_may_throw()самому ділі це згенерує виняток, ви залишаєтеся з витоком пам'яті , тому що пам'ять , виділена для sніколи не буде утилізована. Але знову ж таки, на практиці це навряд чи є проблемою з тієї ж причини, що "Правило 3" вже не є великою проблемою. Дуже рідко (і зазвичай непотрібно) реально керувати власною пам’яттю за допомогою сирих покажчиків. Щоб уникнути вказаної вище проблеми, все, що вам потрібно зробити, - це використовувати std::stringабо std::vector, і деструктор автоматично викличеться під час розмотування стека після викиду винятку.

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

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


Це було правило трьох у C ++ 03, а зараз правило чотирьох у C ++ 11.
DeadMG

1
Ви можете назвати це "Правило 5" для конструктора копій, конструктора переміщення, призначення копії, призначення переміщення та деструктора. Але семантика переміщення не завжди потрібна лише для правильного управління ресурсами.
Чарльз Сальвія

Вам не потрібно окреме завдання переміщення та копіювання. Ідіома копіювання та заміни може робити обох операторів в одному.
DeadMG

2
Це відповідає на питання Does C# avoid pitfalls that are avoided in C++ only by careful programming?. Відповідь "не дуже, тому що уникнути підводних каменів тривожно легко, про що Джоел говорив у сучасному C ++"
Чарльз Сальвія

1
IMO, хоча мови високого рівня, такі як C # або Java, надають вам управління пам’яттю та інші речі, які повинні допомогти вам, але це не завжди так, як належить . Ви все ще доглядаєте за своїм дизайном коду, щоб у вас не залишилося витоку пам’яті (це не зовсім те, що ви б закликали в C ++). З мого досвіду, мені здається, що НАДІЙЩО легше керувати пам’яттю в C ++, оскільки ви знаєте, що деструктори будуть викликані, і в більшості випадків вони очищають. Зрештою, C ++ має розумні покажчики на випадки, коли дизайн не дозволяє ефективно керувати пам'яттю. C ++ чудово підходить, але не для муляжів.
Pijusn

3

Я б дуже не погодився. Можливо, менше підводних каменів, ніж C ++, як це було у 1985 році.

Чи C # уникає підводних каменів, яких у C ++ уникає лише ретельне програмування? Якщо так, то в якій мірі і як їх уникнути?

Не зовсім. Такі правила, як Правило трьох, втратили величезне значення в C ++ 11 завдяки unique_ptrта shared_ptrстандартизованим. Використання класів Standard у нечітко обґрунтованому способі - це не "ретельне кодування", це "базове кодування". Плюс, частка населення С ++, які ще досить дурні, неінформовані або обидва, щоб робити такі речі, як ручне управління пам’яттю, значно нижча, ніж раніше. Реальність полягає в тому, що викладачі, які хочуть продемонструвати такі правила, повинні витрачати тижні, намагаючись знайти приклади, де вони все-таки застосовуються, оскільки класи Класу охоплюють практично кожен можливий випадок використання. Багато ефективних методів C ++ пройшли тим же шляхом - шляхом додо. Багато інших насправді не є специфікою C ++. Дозвольте мені побачити. Пропускаючи перший елемент, наступні десять:

  1. Не кодуйте C ++, як це C. Це дійсно просто здоровий глузд.
  2. Обмежте свої інтерфейси та використовуйте інкапсуляцію. ООП.
  3. Двофазні автори коду ініціалізації повинні бути спалені на корі. ООП.
  4. Знайте, що таке значення семантики. Це справді C ++?
  5. Знову обмежте свої інтерфейси, на цей раз дещо по-іншому. ООП.
  6. Віртуальні деструктори. Так. Це, мабуть, досі діє. finalі overrideдопомогли змінити цю гру на краще. Зробіть свій деструктор, overrideі ви гарантуєте приємну помилку компілятора, якщо ви отримаєте його у спадок від того, хто не зробив їх деструктора virtual. Створіть свій клас, finalі жоден поганий скраб не зможе зібратися і успадкувати його випадково без віртуального деструктора.
  7. Погані речі трапляються, якщо функції очищення виходять з ладу. Це насправді не характерно для C ++ - ви можете побачити однакові поради як для Java, так і для C # - і, ну, майже всі мови. Функції очищення, які можуть вийти з ладу, - це просто погано, і в цьому пункті немає нічого С ++ або навіть OOP.
  8. Будьте в курсі того, як порядок конструктора впливає на віртуальні функції. Весело, що в Java (чи то поточної, так і минулої) вона просто неправильно викликає функцію класу Derived, що ще гірше, ніж поведінка C ++. Незалежно від цього питання не стосується C ++.
  9. Операторські перевантаження повинні вести себе так, як очікують люди. Не дуже конкретний. Пекло, навряд чи навіть оператор перевантажує конкретну, те саме можна застосувати до будь-якої функції - не дайте їй одного імені, а потім змушуйте робити щось зовсім неінтуїтивне.
  10. Це фактично зараз вважається поганою практикою. Усі оператори присвоєння, які є безпечними для винятків, вирішують питання про самопризначення просто, і самопризначення фактично є логічною помилкою програми, а перевірка на самопризначення просто не коштує витрат на продуктивність.

Очевидно, що я не збираюся проходити через кожний окремий елемент C ++, але більшість із них просто застосовують основні поняття до C ++. Ви знайдете ту саму пораду в будь-якій мові оператора, що орієнтується на об'єкт, орієнтований на завантаження. Віртуальні деструктори - це єдиний, який є проблемою C ++ і все ще діє, хоча, мабуть, для finalкласу C ++ 11 він не такий дійсний, як це був. Пам'ятайте, що Ефективний C ++ був написаний тоді, коли ідея застосування OOP та специфічних особливостей C ++ була ще дуже новою. Ці елементи навряд чи стосуються підводних каменів C ++ та більше про те, як впоратися зі зміною з C та як правильно користуватися OOP.

Редагувати: Підводні камені C ++ не включають такі речі, як підводні камені malloc. Я маю на увазі, для одного, кожен окремий підводний камінь, який ви можете знайти в коді C, ви однаково можете знайти в небезпечному коді C #, тому це не особливо актуально, по-друге, тільки те, що Стандарт визначає його для взаємодії, не означає, що його використання вважається C ++ код. Стандарт також визначає goto, але якби ви писали гігантську групу спагеті, використовуючи його, я вважаю, що це ваша проблема, а не мова. Існує велика різниця між "обережним кодуванням" та "Дотримуючись основних ідіом мови".

Чи є нові, різні підводні камені в C #, про які повинен знати новий програміст C #? Якщо так, то чому їх не вдалося уникнути дизайном C #?

usingсмокче. Це дійсно так. І я поняття не маю, чому чогось кращого не було зроблено. Крім того, Base[] = Derived[]і майже все використання Object, яке існує тому, що оригінальні дизайнери не помітили величезного успіху, що шаблони були в C ++, і вирішили, що "Давайте просто все отримаємо у спадок і втратимо всю безпеку нашого типу" - це був розумніший вибір . Я також вважаю, що ви можете знайти неприємні сюрпризи в таких речах, як перегони з делегатами та інші подібні забави. Тоді є й інші загальні речі, як-от те, як дженерики жахливо смокчуть порівняно з шаблонами, справді непотрібне насильницьке розміщення всього в classі подібних речах.


5
Хоча освічена база користувачів або нові конструкції насправді не зменшують мотузку. Вони просто робочі, щоб менше людей опинилося повішеними. Хоча це все хороший коментар щодо Ефективного C ++ та його контексту в еволюції мови.
Теластин

2
Ні. Йдеться про те, як купу елементів в Ефективній C ++ є поняттями, які однаково можуть застосовуватися до будь-якої об'єктно-орієнтованої мови, орієнтованої на значення. Навчання бази даних коду фактичного C ++ замість C, безумовно, зменшує мотузку, яку дає вам C ++. Також я б очікував, що нові мовні конструкції є убуває мотузкою. Йдеться про те, як тільки те, що C ++ Standard визначає malloc, не означає, що ви повинні це робити, більше, ніж просто тому, що ви можете повіяти, gotoяк сука, означає, що це мотузка, з якою ви можете повіситись.
DeadMG

2
Використання частин C на C ++ не відрізняється від написання всього коду unsafeна C #, що так само погано. Я міг би перерахувати кожен підводний код кодування C #, як і C, якщо ви хочете.
DeadMG

@DeadMG: тож справді питання має бути "у програміста C ++ є достатньо мотузки, щоб він повісився, поки він є програмістом на C"
gbjbaanb

"Крім того, частка населення C ++, які ще досить дурні, неінформовані або обидва, щоб робити такі речі, як ручне управління пам'яттю, набагато нижча, ніж раніше". Потрібне цитування.
dan04

3

Чи C # уникає підводних каменів, яких у C ++ уникає лише ретельне програмування? Якщо так, то в якій мірі і як їх уникнути?

C # має переваги:

  • Не є сумісним із C на зворотному рівні, тим самим уникнути наявності у списку "злих" мовних особливостей (наприклад, сирих вказівників), які синтаксично зручні, але зараз вважаються поганим стилем.
  • Маючи довідкову семантику замість семантики значень, яка складає щонайменше 10 ефективних елементів C ++, а також вводить нові підводні камені.
  • Маючи меншу поведінку, визначену реалізацією, ніж C ++.
    • Зокрема, в C ++ кодування символів char, stringі т.д. залежить від реалізації. Розкол між підходом Windows до Unicode ( wchar_tдля UTF-16, charдля застарілих "кодових сторінок") та підходом * nix (UTF-8) викликає великі труднощі в кросплатформенному коді. C #, OTOH гарантує, що a stringє UTF-16.

Чи є нові, різні підводні камені в C #, про які повинен знати новий програміст C #?

Так: IDisposable

Чи існує еквівалентна книга "Ефективний C ++" для C #?

Існує книга під назвою Ефективний C #, яка за структурою схожа на Ефективна С ++ .


0

Ні, C # (і Java) менш безпечні, ніж C ++

C ++ є перевіряється на місцях . Я можу оглянути один клас у C ++ та визначити, що клас не просочується пам'яттю чи іншими ресурсами, припускаючи, що всі посилаються класи правильні. У Java або C # необхідно перевіряти кожен посилається клас, щоб визначити, чи потребує певного доопрацювання.

C ++:

{
   some_resource r(...);  // resource initialized
   ...
}  // resource destructor called, no leaks here

C #:

{
   SomeResource r = new SomeResource(...); // resource initialized
   ...
} // did I need to finalize that?  May I should have used 'using' 
  // (or in Java, a grotesque try/finally construct)?  No way to tell
  // without checking the documentation for SomeResource

C ++:

{
    auto_ptr<SomeInterface> i = SomeFactory.create(...);
    i->f(...);
} // automatic finalization and memory release.  A new implementation of
  // SomeInterface can allocate and free resources with no impact
  // on existing code

C #:

{
   SomeInterface i = SomeFactory.create(...);
   i.f(...);
   ...
} // Sure hope someone didn't create an implementation of SomeInterface
  // that requires finalization.  In C# and Java it is necessary to decide whether
  // any implementation could require finalization when the interface is defined.
  // If the initial decision is 'no finalization', then no future implementation  
  // can acquire any resource without creating potential leaks in existing code.

3
... досить суттєво в сучасних ІДЕ визначити, чи щось успадковується від IDisposable. Основна проблема полягає в тому , що вам потрібно знати , щоб використання auto_ptr(або деякі з його родичів). Це прислів’я мотузка.
Теластин

2
@Telastyn ні, справа в тому, що ви завжди використовуєте розумний вказівник, якщо тільки ви дійсно не знаєте, що він вам не потрібен. У C # використання заяви - це як мотузка, на яку ви посилаєтесь. (тобто в C ++ ви повинні пам’ятати, щоб використовувати розумний вказівник, чому тоді C # не так вже й погано, хоча вам потрібно пам’ятати завжди використовувати оператор, що використовує)
gbjbaanb

1
@gbjbaanb Тому що? 5% у більшості класів C # є одноразовими? І ви знаєте, що вам потрібно розпоряджатися ними, якщо вони одноразові. У C ++ кожен окремий об'єкт є одноразовим. І ви не знаєте, чи потрібно вирішувати ваш конкретний екземпляр. Що відбувається з поверненням покажчиків, які не з заводу? Чи є ваша відповідальність за їх очищення? Це не повинно бути, але іноді є. І знову ж таки, тому, що ви завжди повинні використовувати розумний вказівник, не означає, що варіант не припиняється. Особливо для початківців це суттєвий підводний камінь.
Теластин

2
@Telastyn: Знати використовувати так auto_ptrсамо просто, як знати, IEnumerableчи використовувати інтерфейси, або не використовувати плаваючу крапку для валюти чи іншого. Це основне застосування DRY. Ніхто, хто знає основи програмування, не допустить цієї помилки. На відміну від using. Проблема usingполягає в тому, що ви повинні знати для кожного класу, чи є він одноразовим (і я сподіваюся, що ніколи, ніколи не змінюється), і якщо він не є одноразовим, ви автоматично забороняєте всі похідні класи, які, можливо, повинні бути одноразовими.
DeadMG

2
Кевін: Ага, ваша відповідь не має сенсу. Це не вина C #, що ви робите це неправильно. Ви ж НЕ залежите від фіналізатор в правильно написаних C # код . Якщо у вас є поле з Disposeметодом, ви повинні реалізувати IDisposable("правильний" спосіб). Якщо ваш клас робить це (що еквівалент впровадженню RAII для вашого класу в C ++), а ви використовуєте using(що подібно розумним покажчикам у C ++), це все працює чудово. Фіналізатор в основному призначений для запобігання нещасних випадків - Disposeвідповідає за правильність, і якщо ви не користуєтесь ним, ну, це ваша вина, а не C #.
користувач541686

0

Так, на 100% так, як я вважаю, що неможливо звільнити пам'ять і використовувати її в C # (припустимо, що вона керована, і ви не переходите в небезпечний режим).

Але якщо ви знаєте, як програмувати на C ++, чого неймовірна кількість людей не робить. Ви майже добре. Як і класи Чарльза Сальвії, насправді не керують своїми спогадами, оскільки все це обробляється в попередніх класах STL. Я рідко використовую покажчики. Насправді я пішов на проекти, не використовуючи жодного вказівника. (C ++ 11 полегшує це).

Що стосується помилок, дурних помилок і т. Д. (Напр .: if (i=0)bc ключ застряг, коли ви натискаєте == дуже швидко), компілятор скаржиться, що приємно, оскільки це покращує якість коду. Інший приклад - це забути breakв операторах переключення і не дозволяти вам оголошувати статичні змінні у функції (що мені не подобається іноді, але це гарна ідея imo).


4
Java та C # погіршили =/ ==проблему, використовуючи ==для еталонної рівності та введення .equalsдля ціннісної рівності. Тепер бідний програміст повинен відслідковувати, чи є змінною "подвійний" або "Подвійний", і обов'язково зателефонуйте правильний варіант.
Кевін Клайн

@kevincline +1, але в C # structви можете зробити це, ==що працює неймовірно добре, оскільки в більшості випадків в одному є лише рядки, ints та floats (тобто лише структури структури). У своєму власному коді я ніколи не відчуваю цієї проблеми, за винятком випадків, коли хочу порівняти масиви. Я не думаю, що я ніколи не порівнюю типи списку чи неструктури (рядок, int, float, DateTime, KeyValuePair та багато інших)

2
Python отримав це право, використовуючи ==для ціннісної рівності та isдля еталонної рівності.
dan04

@ dan04 - Скільки видів рівності, на вашу думку, має C #? Дивіться чудову розмову про блискавку ACCU: Деякі об'єкти рівніші за інші
Марк Бут
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.