Zalecanie korzystania z kontenerów zamiast dynamicznej alokacji w C++

4

Sytuacja ta powtarza się na tym forum od lat:

  • Przychodzi nowicjusz, mający wyraźne problemy z podstawami C++, i prezentuje swój kod upstrzony rozmaitymi new, delete, malloc, tab[i], *(ptr+n), tablic znaków, itd, itp.
  • Informuje się go, że to jest zła praktyka w C++ i ma zamiast tego korzystać z, niepotrzebne skreślić: std::vector, std::string, unique_ptr, shared_ptr, etc, etc.

Sytuacja ta wynika z faktu, że programy nauczania w szkołach, a być może (nie wiem) także na niektórych uczelniach, notorycznie:

  • Wymagają od uczniów / studentów znajomości programowania imperatywnego na poziomie C, a zatem w szczególności: dynamicznej alokacji, tablic, arytmetyki wskaźników;
  • Jednocześnie nauczają tego w C++, ponieważ:
    • C++ udostępnia struktury, które są w wielu miejscach wygodniejsze w użyciu, aniżeli C (np. łatwiej jest nauczyć kogoś korzystania z std::cout aniżeli printf);
    • Kiedy już nauczy się tablic, można wprowadzić klasy, w tym kontenery bez konieczności zmiany języka.

Innymi słowy: Chcąc / musząc nauczyć dynamicznej alokacji, wybierają do tego takie narzędzie, które po pierwsze to umożliwia, po drugie jest stosunkowo najwygodniejsze spośród tych, które to umożliwiają.

Z jednej strony rozumiem forumowiczów, których bije po oczach to, co z ich perspektywy jest "nauczaniem złych praktyk w C++". Z drugiej strony - choć to zabrzmi pewnie jak herezja - rozumiem także nauczycieli, którzy chcą przerobić wymagany materiał bez dodatkowych utrudnień, rozpraszających uwagę ucznia od tej konkretnie wiedzy, którą chcą mu wbić do głowy.

NIE rozumiem natomiast radzenia takim nowicjuszom, by zaczęli używać kontenerów / unique_ptr / etc zamiast operowania na czystych, dynamicznie zaalokowanych wskaźnikach! Jeśli - zgodnie z tymi radami - przestaną uczyć się dynamicznej alokacji itp, to nie wykażą się wiedzą, której się od nich wymaga. Czy nam się to podoba, czy nie: Jeśli każe się im używać new, delete, itp, to muszą nauczyć się tego używać.

Oczywiście, można radzić im np. pisać w C a nie w C++, skoro już muszą operować na "czystych", dynamicznie zaalokowanych wskaźnikach. Jednak:

  • W zasadzie nawet i to rodzi podobne wątpliwości, no bo w C są VLA, więc czy dobrą praktyką jest stosowanie dynamicznych alokacji zamiast VLA?
    • Ale idąc tym tropem: Jak w ogóle można nauczyć kogoś dynamicznej alokacji, jednocześnie nie ucząc go mitycznych "złych praktyk" ani nie rozpraszając jego uwagi zadaniem na tyle skomplikowanym, by VLA nie mogło się sprawdzić?
  • Moim zdaniem, pisanie w C zamiast w C++ przez takiego ucznia to byłoby już, z jego perspektywy, byciem plus catholique que le pape...

Jak można mu wytłumaczyć, dlaczego pisanie *(ptr+offset) ma się jednocześnie wykluczyć z pisaniem cout << wynik << endl, a wymuszać pisanie printf("%d\n", wynik) - ah uwaga na pomyłki jeśli wynik jest long long a nie int

A zresztą, w zasadzie po co? Bo mistyczna "jakość kodu"? Jednak tu jest jakieś przemieszanie, ponieważ żeby wiedzieć, jak należy pisać kod, żeby był elegancki, "wysokiej jakości", żeby wbić komuś do głowy wszystkie patterny, antipatterny, itp, to trzeba najpierw nauczyć go, czym w ogóle jest programowanie i jak ma pisać kod, żeby w ogóle działał!

To, do czego - moim zdaniem - można rzeczywiście przyczepić się programom nauczania, to to, że zaczynają od nauczania programowania na poziomie C. Jakiś doświadczony programista mi kiedyś w cztery oczy powiedział, że uważa to za pomyłkę, i że należy zaczynać od C# albo Javy - a na inne wynalazki, w tym C czy C++, przyjdzie czas później - żaden z tych dwóch języków nie nadaje się dla początkujących. Rozumiem ten punkt widzenia; jednak wyrzucenie arytmetyki wskaźników i dynamicznej alokacji z programów nauczania dla liceum to chyba marzenie ściętej głowy.

Póki zatem tak się nie stanie, proponuję przestać wreszcie czepiać się nowicjuszy, potrzebujących pomocy z materiałem, którego wymaga od nich szkoła.

0

TL;DR

kmph napisał(a):

Póki zatem tak się nie stanie, proponuję przestać wreszcie czepiać się nowicjuszy, potrzebujących pomocy z materiałem, którego wymaga od nich szkoła.

Dokładnie, ciągle w komentarzach zwracam na to uwagę odpowiadającym. Niektórzy nie rozumieją, że mus to mus.

0
kmph napisał(a):
  • W zasadzie nawet i to rodzi podobne wątpliwości, no bo w C są VLA, więc czy dobrą praktyką jest stosowanie dynamicznych alokacji zamiast VLA?

VLA są na stosie, który jest ograniczony rozmiarowo (do np. 1 MB) i czasowo (nie możemy zwrócić wskaźnika do VLA, bo tablica zostanie zniszczona).
Jestem zwolennikiem VLA, ale ich przydatność jest ograniczona.

0
Azarien napisał(a):

VLA są na stosie, który jest ograniczony rozmiarowo (do np. 1 MB) i czasowo (nie możemy zwrócić wskaźnika do VLA, bo tablica zostanie zniszczona).
Jestem zwolennikiem VLA, ale ich przydatność jest ograniczona.

Tak oczywiście, ale sformułowanie zadania w ten sposób, by w C nie dało się go zrobić na VLA skomplikuje to zadanie i odwróci uwagę od jego rzeczywistej treści, czyli dynamicznej alokacji. Tymczasem, AFAIK, dobrą praktyką pedagogiczną jest, by wprowadzając jakiś temat (np. dynamiczną alokację) zacząć od zadań TYLKO na dynamiczną alokację, bez dodatkowych trudności. A z kolei, na podstawie krytykowanego przeze mnie rozumowania, które widzimy często na forum, to takiego zadania pewnie NIE NALEŻAŁOBY rozwiązać poprzez dynamiczną alokację, a poprzez zastosowanie VLA, bo to lepsza praktyka.

Mamy więc sytuację, w której praktycznie nie da się sformułować treści zadania tak, by zastosowanie w rozwiązaniu dynamicznej alokacji było poprawne.

2
kmph napisał(a):

Mamy więc sytuację, w której praktycznie nie da się sformułować treści zadania tak, by zastosowanie w rozwiązaniu dynamicznej alokacji było poprawne.

Wracając do C++, typowym zadaniem (albo ćwiczeniem) jest by napisać klasę, która dynamicznie alokuje obszar pamięci, zwalnia go w destruktorze, i kopiuje kiedy trzeba.
Ale rozwiązanie zaraz zostanie skrytykowane że teraz to przecież nie rule of 3, tylko rule of 5, a tak naprawdę to smartpointery i rule of 0, a w ogóle to wszystko powinno być template'em.

(to ostatnie mnie szczególnie irytuje - często przykłady jak coś zrobić są zaciemnione przez to że wszystko jest stemplejtowane, choć template'y zupełnie nie są przedmiotem rozważań)

7

Silnie się nie zgadzam, i nie podoba mi się wyrażona w poście implikacja celowej złośliwości.

Z mojej perspektywy sytuacja wygląda tak:

Przychodzi nowicjusz z kodem naszpikowanym złymi praktykami, który jest nieczytelny, trudny do modyfikacji/utrzymania i, w ogólnym rozrachunku, sprawia, że postawione zadanie jest trudniejsze. Nie ma żadnego dowodu na to, że:

  • jest studentem, a nie samoukiem, który trafił na kurs
  • jeśli jest studentem, to na jego uczelni nie wolno uczyć się samodzielnie
  • a na egzaminie jest wymagane rozwiązanie z tablicy, bo inne nie zostanie uznane

Taki nowicjusz dostaje poradę, aby zaczął programować biorąc pod uwagę, że jest get_current_year(), a nie 1995, nie korzystał z błędnych poradników/tutoriali itd. Tutaj warto podkreślić, że rzadko jest to całość porady. Jeśli podany kod nie nadaje się na kandydata na IOCCC, lub ma porządne MCVE, to dostaje również bezpośrednie pytanie zadane w poście.

W mojej subiektywnej opinii (nie prowadziłem statystyk, więc mogę się mylić) mniej niż 50% odpowiada "ale na studiach tak karzo". Jeśli jednak już tak się stanie, to nikt im głowy nie urywa.


Na to, że program uczelni (albo kursów, tutoriali, książek) bywa beznadziejny nie jesteśmy w stanie wiele poradzić, ale nie ma absolutnie nic złego w informowaniu newbów, że istnieją lepsze rozwiązania niż używane przez nich obecnie. Przez podejście z pierwszego posta potem tacy "programiści" we własnych programach, albo już w pracy zawodowej, piszą własną implementację wektora (albo lepiej, listy), bo nikt im nawet nie wspomniał o tym, że w bibliotece standardowej jest już poprawna implementacja. Albo brzydzą się C++, bo jedyne co o nim wiedzą to mnóstwo new, delete i crashy spowodowanych błędnym ich użyciem.

Utrzymywanie ludzi w ignorancji, bo być może nie będą mogli natychmiastowo użyć nowej wiedzy, uważam za jednoznacznie szkodliwe.

1

Szkoły i uczelnie często nie rozróżniają C od C++, ponieważ tak łatwiej.
Ale to nie znaczy że uczniowie tak muszą pisać.
Nikt nikomu na uczelni nie zwróci uwagi i nie obniży oceny jeśli zamiast printf użyje w C++ cout.
Dbanie o rozdzielność tych dwóch języków jest pożyteczna, ponieważ edukuje ludzi nie tylko początkujących ale także wszystkich którzy to czytają.
Można się czepiać stylu w jakim się zwraca uwagę, ale są ludzie i ludziska.
Aktualnie C++ przeżywa dynamiczny rozwój i często nawet konsultanci tego języka nie są do końca zgodni co jest lepsze.
Od tego jest forum żeby takie poglądy wymieniać.

Moderatorem forum C++ jest @kq, którego uważam za jedną najbardziej wyważonych osobę na forum, więc czepiać się można ew. ludzi, którzy
niedawno dołączyli i próbują budować swoje ego kosztem innych. I takie zachowania trzeba potępiać niezależnie od języka.

2

Rozwiązanie tego problemu jest proste – odpowiedzieć na temat, sugerując wykonanie w taki sposób, którego oczekują nauczyciele/prowadzący. Natomiast dodatkowo i wyraźnie poinformować o bolączkach danego rozwiązania, wyjaśnić dlaczego jest złe i jak rozwiązanie powinno wyglądać, jeśli chodzi o współczesne standardy tworzenia kodu. Wilk syty i owca cała.

Użytkownicy przebywający tutaj znaczną ilość czasu potrafią wyłapać uczniaków bez dopytywania, natomiast masa w miarę nowych użytkowników tego nie potrafi i na siłę forsuje swoje pomysły, jednocześnie nie przyjmując do wiadomości, że nie za bardzo pomaga.

0

@kq:

Ale w tym momencie wracamy do punktu wyjścia: Przy prezentowanym przez Ciebie podejściu nie ma praktycznej możliwości zrobienia zadania edukacyjnego na dynamiczną alokację, gdyż albo będzie to zadanie na multum innych rzeczy oprócz dynamicznej alokacji, albo uznasz, że dynamiczna alokacja jest nieprawidłowym rozwiązaniem!

Oczywiście, że istnieje std::vector i bezwzględnie należy o jego istnieniu poinformować.

Ale idąc tym tropem: Oczywiście istnieje także std::sort, a nawet bibliotece standardowej C mamy qsort - jednak ich istnienie nie oznacza, że nie można dać zadania napisz własne sortowanie. A przecież idąc tym tropem można uznać, że rozwiązanie zadania napisz własne sortowanie poprzez wklepanie mergesorta, quicksorta, a na jeszcze wcześniejszych etapach nauki nawet bubblesorta jest nieprawidłowe, bo należy użyć sortowania z biblioteki standardowej.

Oczywiście, że kod pisany przez licealistów nie będzie nadawał się na produkcję. Bo też i nadawać się nie może! Nie da się od razu nauczyć delikwenta tyle, by pisał kod nadający się na produkcję! Dlatego też zaczynamy od zadań sztucznych i dopuszczamy rozwiązania sztuczne.

Tym samym rozumowaniem można zabraniać uczyć absolutnego newbiego pisania pętli. Przecież nawet C++ ma #include <algorithm>, o wynalazkach rodem z nowszych języków takich jak LINQ w C# już nie wspominając. Ich należy używać zamiast pisania pętli od ręki! A jednak słusznie zaczyna się programowanie od trywialnych pętli na tablicach.

1

Jeśli student czy nawet samouczeń nie przećwiczy dynamicznej alokacji polegając tylko na kontenerach, to nigdy nie będzie jej umiał a działanie takiego std::vector pozostanie dla niego magią.

2
kmph napisał(a):

Ale w tym momencie wracamy do punktu wyjścia: Przy prezentowanym przez Ciebie podejściu nie ma praktycznej możliwości zrobienia zadania edukacyjnego na dynamiczną alokację, gdyż albo będzie to zadanie na multum innych rzeczy oprócz dynamicznej alokacji, albo uznasz, że dynamiczna alokacja jest nieprawidłowym rozwiązaniem!

Musisz mieć na myśli jakieś bardzo dziwne rozwiązania, bo zadanie typu "napisz własną implementację dynamicznej tablicy/listy [12]-kierunkowej" jest jak najbardziej sensowne z mojej perspektywy. Przynajmniej jeśli chodzi o naukę struktur danych, bo na pewno nie w kursie podstaw C++.

Ale idąc tym tropem: Oczywiście istnieje także std::sort, a nawet bibliotece standardowej C mamy qsort - jednak ich istnienie nie oznacza, że nie można dać zadania napisz własne sortowanie. A przecież idąc tym tropem można uznać, że rozwiązanie zadania napisz własne sortowanie poprzez wklepanie mergesorta, quicksorta, a na jeszcze wcześniejszych etapach nauki nawet bubblesorta jest nieprawidłowe, bo należy użyć sortowania z biblioteki standardowej.

Idąc tym tropem: uczymy się tutaj języka, czy podstaw programowania/algorytmiki/struktur danych? Przez takie mieszanie pojęć później mamy kod produkcyjny z bubblesortem, bo niedouczony programista nie wiedział, że jest std::sort. Np. w Pythonie coś takiego się nie zdarza, bo nikt o zdrowych zmysłach nie uczy bubblesorta w tym języku, a nawet jeśli - to nie przed użyciem sorted z biblioteki standardowej.

Oczywiście, że kod pisany przez licealistów nie będzie nadawał się na produkcję. Bo też i nadawać się nie może! Nie da się od razu nauczyć delikwenta tyle, by pisał kod nadający się na produkcję! Dlatego też zaczynamy od zadań sztucznych i dopuszczamy rozwiązania sztuczne.

Co nie oznacza, że powinniśmy celowo upośledzać ich naukę poprzez naukę złych praktyk. Kursu jazdy samochodem nie zaczynasz od budowy 18-wiecznego silnika parowego. Dlaczego chcesz zaczynać kurs C++ od nauki korzystania z przestarzałych i zbędnych (a czasem nawet po prostu błędnych) rozwiązań?

Tym samym rozumowaniem można zabraniać uczyć absolutnego newbiego pisania pętli. Przecież nawet C++ ma #include <algorithm>, o wynalazkach rodem z nowszych języków takich jak LINQ w C# już nie wspominając. Ich należy używać zamiast pisania pętli od ręki! A jednak słusznie zaczyna się programowanie od trywialnych pętli na tablicach.

Brak mi danych eksperymentalnych, aby oceniać czy rzeczywiście tak słusznie.

Azarien napisał(a):

Jeśli student czy nawet samouczeń nie przećwiczy dynamicznej alokacji polegając tylko na kontenerach, to nigdy nie będzie jej umiał a działanie takiego std::vector pozostanie dla niego magią.

Nie uważam, aby taka wiedza była początkującym niezbędna.


Ta prelekcja generalnie podsumowuje to co mówię:

0

ten artykuł niedługo będzie też o programistach C++ , tak jak już jest o tych co twierdzą że znają C#
https://www.joelonsoftware.com/2005/12/29/the-perils-of-javaschools-2/

2

Są różne rodzaje zadań robionych w C++:

  • nauka programowania
  • nauka algorytmów i struktur danych
  • nauka C++

Zadań z C-podobnej alokacji w C++ raczej mało tu jest.
Trzeba umieć rozróżniać te zadania. Każde z nich wymaga innego podejścia.

2

Wszystko fajnie, tylko o tym, że dane zadanie ma jakieś dziwne obostrzenia dowiadujemy się zazwyczaj w drugim czy kolejnym poście gdy ktoś już przedstawi współczesne rozwiązanie.

A to, że ktoś czasem wybuchnie(co mi się zdarzyło, niepotrzebnie, mogę się jedynie usprawiedliwić paskudnym humorem) to też nic niezwykłego, bo mylenie C z C++ jest nagminne, niczym bynajmniej/przynajmniej.

No i też jestem ciekawy ilu uczniów/studentów wie, że new może trzasnąć wyjątek oraz, że ma wersję, która zwraca nullptr. Tak ich maglują z tą dynamiczną alokacją, że to przecież powinna być oczywista oczywistość.

1

Bez przesady, to nie jest forum uczelniane.
Sam jakiś ładny czas temu założyłem temat, otrzymałem odpowiedź, że tego i tamtego w C++ się nie robi/jest na to lepszy sposób, ale po mojej odpowiedzi, że wymaga ode mnie prowadzący - dostałem pomoc dopasowaną od wymagań.

To tak jakby mieć pretensje, że komuś poleca się jazdę samochodem, gdy próbuje przejść dłuższą trasę na piechotę[chociaż tutaj byłby przynajmniej jakiś pożytek zdrowotny. ;) ]

1 użytkowników online, w tym zalogowanych: 0, gości: 1