Próbuję zrozumieć różnicę między językami proceduralnymi, takimi jak C, a językami obiektowymi, takimi jak C ++. Nigdy nie korzystałem z C ++, ale rozmawiałem z przyjaciółmi o tym, jak je rozróżnić.
Powiedziano mi, że C ++ ma koncepcje obiektowe, a także tryby publiczne i prywatne do definiowania zmiennych: rzeczy, których C nie ma. Nigdy nie musiałem ich używać do tworzenia programów w Visual Basic.NET: jakie są z tego korzyści?
Powiedziano mi również, że jeśli zmienna jest publiczna, można uzyskać do niej dostęp w dowolnym miejscu, ale nie jest jasne, jak różni się ona od zmiennej globalnej w języku takim jak C. Nie jest również jasne, w jaki sposób zmienna prywatna różni się od zmiennej lokalnej.
Inną rzeczą, którą usłyszałem, jest to, że ze względów bezpieczeństwa, aby uzyskać dostęp do funkcji, należy ją najpierw odziedziczyć. Przypadek użycia polega na tym, że administrator powinien mieć tylko tyle praw, ile potrzebuje, a nie wszystko, ale wydaje się, że warunek też by działał:
if ( login == "admin") {
// invoke the function
}
Dlaczego to nie jest idealne?
Biorąc pod uwagę, że wydaje się, że istnieje proceduralny sposób wykonywania wszystkiego zorientowanego obiektowo, dlaczego miałbym się przejmować programowaniem obiektowym?
źródło
Odpowiedzi:
Wszystkie dotychczasowe odpowiedzi koncentrowały się na temacie twojego pytania, jak stwierdzono: „jaka jest różnica między c i c ++”. W rzeczywistości wygląda na to, że wiesz, czym jest różnica, po prostu nie rozumiesz, dlaczego taka różnica jest potrzebna. Zatem inne odpowiedzi próbowały wyjaśnić OO i enkapsulację.
Chciałem odpowiedzieć na jeszcze jedną odpowiedź, ponieważ na podstawie szczegółów twojego pytania uważam, że musisz cofnąć się o kilka kroków.
Nie rozumiesz celu C ++ lub OO, ponieważ wydaje ci się, że Twoja aplikacja po prostu musi przechowywać dane. Te dane są przechowywane w zmiennych. „Dlaczego miałbym chcieć, aby zmienna była niedostępna? Teraz nie mam już do niej dostępu! Dzięki upublicznieniu wszystkiego, a jeszcze lepiej globalizacji, mogę czytać dane z dowolnego miejsca i nie ma żadnych problemów”. - I masz rację, biorąc pod uwagę skalę projektów, które obecnie piszesz, prawdopodobnie nie ma tak wielu problemów (lub jest ich, ale po prostu jeszcze ich nie zauważyłeś).
Myślę, że podstawowe pytanie, na które naprawdę musisz odpowiedzieć, brzmi: „Dlaczego miałbym kiedykolwiek chcieć ukryć dane? Jeśli to zrobię, nie będę w stanie z tym pracować!” I dlatego:
Załóżmy, że zaczynasz nowy projekt, otwierasz edytor tekstu i zaczynasz pisać funkcje. Za każdym razem, gdy musisz coś zapisać (aby zapamiętać na później), tworzysz zmienną. Aby uprościć sprawę, zmienne są globalne. Twoja pierwsza wersja Twojej aplikacji działa świetnie. Teraz zaczynasz dodawać więcej funkcji. Masz więcej funkcji, niektóre dane, które wcześniej zapisałeś, muszą zostać odczytane z nowego kodu. Inne zmienne wymagają modyfikacji. Ciągle piszesz więcej funkcji. To, co mogłeś zauważyć (a jeśli nie, to absolutnie zauważysz w przyszłości), to fakt, że w miarę powiększania się kodu, dodanie kolejnej funkcji zajmuje coraz więcej czasu. W miarę powiększania się kodu, coraz trudniej jest dodawać funkcje bez zepsucia czegoś, co kiedyś działało. Dlaczego? Ponieważ musisz pamiętać, co wszystkotwoje zmienne globalne są przechowywane i musisz pamiętać, gdzie wszystkie są modyfikowane. I musisz pamiętać, która funkcja jest w porządku, aby wywoływać w dokładnej kolejności, a jeśli wywołasz je w innej kolejności, możesz otrzymać błędy, ponieważ twoje zmienne globalne nie są jeszcze poprawne. Czy kiedykolwiek na to wpadłeś?
Jak duże są twoje typowe projekty (wiersze kodu)? Teraz obrazuje projekt 5000 do 50000 razy większy niż twój. Ponadto pracuje w nim wiele osób. Jak wszyscy członkowie zespołu mogą pamiętać (a nawet być świadomi) tego, co robią wszystkie te zmienne?
To, co opisałem powyżej, jest przykładem doskonale sprzężonego kodu. A od zarania dziejów (zakładając, że czas zaczął się 1 stycznia 1970 r.) Ludzkość szuka sposobów na uniknięcie tych problemów. Aby tego uniknąć, należy podzielić swój kod na systemy, podsystemy i komponenty oraz ograniczyć liczbę funkcji mających dostęp do dowolnej części danych. Jeśli mam 5 liczb całkowitych i ciąg znaków reprezentujący pewien stan, czy łatwiej byłoby mi pracować z tym stanem, gdyby tylko 5 funkcji ustawiło / otrzymało wartości? lub jeśli 100 funkcji ustawia / otrzymuje te same wartości? Nawet bez języków OO (tj. C) ludzie ciężko pracowali nad izolowaniem danych od innych danych i tworzeniem czystych granic separacji między różnymi częściami kodu. Gdy projekt osiągnie określony rozmiar, łatwość programowania staje się niemożliwa: „czy mogę uzyskać dostęp do zmiennej X z funkcji Y”,
Właśnie dlatego wprowadzono koncepcje OO i dlatego są tak potężne. Pozwalają ukryć twoje dane przed sobą i chcesz to zrobić celowo, ponieważ im mniej kodu widzi te dane, tym mniejsza szansa, że kiedy dodasz następną funkcję, coś zepsujesz. Jest to główny cel koncepcji enkapsulacji i programowania OO. Pozwalają rozbić nasze systemy / podsystemy na jeszcze bardziej szczegółowe pola, do tego stopnia, że bez względu na to, jak duży jest cały projekt, do danego zestawu zmiennych można uzyskać dostęp tylko za pomocą 50-200 linii kodu i to wszystko! Programowanie OO ma oczywiście znacznie więcej, ale w gruncie rzeczy właśnie dlatego C ++ oferuje opcje deklarowania danych / funkcji jako prywatne, chronione lub publiczne.
Drugim największym pomysłem w OO jest koncepcja warstw abstrakcji. Chociaż języki proceduralne mogą mieć również abstrakcje, w C programista musi podjąć świadomy wysiłek, aby utworzyć takie warstwy, ale w C ++, kiedy deklarujesz klasę, automatycznie tworzysz warstwę abstrakcji (to nadal zależy od tego, czy ta abstrakcja będzie, czy nie) doda lub usunie wartość). Powinieneś przeczytać / zbadać więcej na temat warstw abstrakcji, a jeśli masz więcej pytań, jestem pewien, że to forum z przyjemnością na nie odpowie.
źródło
Hmm ... może najlepiej jest wykonać kopię zapasową i spróbować przedstawić podstawowe założenia programowania obiektowego. Programowanie obiektowe w dużej mierze ma na celu umożliwienie tworzenia abstrakcyjnych typów danych. Dla naprawdę prostego przykładu, z którym jesteś niewątpliwie zaznajomiony, rozważ ciąg. Łańcuch zwykle ma bufor do przechowywania zawartości łańcucha, niektóre funkcje, które mogą działać na łańcuchu (wyszukiwanie w nim, uzyskiwanie dostępu do jego części, tworzenie podciągów itp.) Będzie także (przynajmniej zwykle) coś do śledź (bieżącą) długość łańcucha i (prawdopodobnie) rozmiar bufora, więc jeśli (na przykład) zwiększysz rozmiar łańcucha z 1 do 1000000, będzie wiedział, kiedy potrzebuje więcej pamięci, aby pomieścić większy zawartość.
Te zmienne (bufor, aktualna długość i rozmiar bufora) są prywatne do samego łańcucha, ale oni nie lokalne dla danej funkcji. Każdy ciąg ma treść o określonej długości, dlatego musimy śledzić jego treść / długość dla tego ciągu. I odwrotnie, ta sama funkcja (np. W celu wyodrębnienia podłańcucha) może działać na wielu różnych ciągach w różnych momentach, więc dane nie mogą być lokalne dla poszczególnych funkcji.
W rezultacie otrzymujemy pewne dane prywatne dla łańcucha, więc są one (bezpośrednio) dostępne dla funkcji łańcucha. Świat zewnętrzny może uzyskać długość łańcucha za pomocą funkcji łańcucha, ale nie musi nic wiedzieć o wewnętrznych elementach łańcucha, aby go uzyskać. Podobnie, może modyfikować ciąg - ale znowu robi to za pomocą funkcji ciągu i tylko one bezpośrednio modyfikują te zmienne lokalne dla obiektu ciągu.
Jeśli chodzi o bezpieczeństwo, zauważę, że chociaż jest to rozsądne jako analogia, nie tak to działa. W szczególności dostęp w C ++ nie ma na celu spełnienia tego samego rodzaju wymagań co dostęp w systemie operacyjnym. System operacyjny powinien egzekwować ograniczenia, aby (na przykład) normalny użytkownik nie mógł robić rzeczy zarezerwowanych dla administratora. Natomiast kontrola dostępu w C ++ ma na celu wyłącznie zapobieganie wypadkom. Z założenia każdy, kto chce je łatwo ominąć. Są w tej samej kolejności, co oznaczanie pliku jako tylko do odczytu, więc nie można go przypadkowo usunąć. Jeśli zdecydujesz się usunąć plik, zmiana go z trybu tylko do odczytu na tryb do odczytu i zapisu jest banalna; ustawienie na „tylko do odczytu” powoduje, że przynajmniej pomyślisz o tym przez sekundę i zdecydujesz się usunąć plik, aby nie został przypadkowo usunięty po uderzeniu niewłaściwego klucza w niewłaściwym czasie.
źródło
OOP kontra C tak naprawdę nie dotyczy żadnej z omawianych kwestii. Chodzi przede wszystkim o pakowanie kodu w obszary, które nie wpływają / nie mogą przypadkowo (a czasem nawet celowo) na siebie wpływać.
C pozwala w zasadzie uruchomić dowolną funkcję z dowolnego miejsca. OOP zapobiega temu poprzez grupowanie metod w klasy i zezwalanie na korzystanie z metod tylko przez odwołanie się do klasy je zawierającej. Tak więc jedną potencjalnie dużą zaletą OOP jest to, że znacznie bardziej prawdopodobne jest, że będziesz mieć lepszą aranżację kodu bez dużego doświadczenia, aby powiedzieć, że powinieneś.
źródło
Dobrze napisana klasa powinna być małą „wyspą zaufania”: możesz jej użyć i założyć, że robi to „właściwą rzecz” i że chroni cię przed typowymi pułapkami. To sprawia, że dobra klasa jest elementem składowym, który jest o wiele bardziej przydatny jako zestaw funkcji i zmiennych, które mogą działać dobrze, ale pokazują wszystkie ich brzydkie odwagi i zmuszają cię do zrozumienia, jak działają razem, jak należy je inicjalizować itp. Dobra klasa powinna być jak wtyczka USB, podczas gdy rozwiązanie proceduralne jest jak wiązka drutów, chipów, cyny i bitu lutowniczego.
Jednym z zagadnień, które nie zostały szczegółowo omówione, jest aspekt interfejsu / implementacji. Interfejs opisuje zachowanie, ale nie realizację. Interfejs listy opisuje tę koncepcjęlisty i jej zachowanie: można się spodziewać metod dodawania, usuwania i określania rozmiaru. Obecnie istnieje wiele różnych sposobów implementacji tej listy, np. Jako lista połączona lub przy użyciu bufora tablicy. Moc programowania OO polega na tym, że za pomocą interfejsu możesz rozumować zachowanie bez wiedzy o implementacji. Uzyskiwanie dostępu do wewnętrznych zmiennych lub metod zniszczyłoby tę abstrakcję, nie można zastąpić jednej implementacji listy inną i nie można ulepszyć istniejącej implementacji bez dotykania kodu za pomocą klasy. To jeden z głównych powodów, dla których potrzebne są prywatne zmienne i metody: aby chronić wewnętrzne szczegóły implementacji, aby abstrakcja pozostała nienaruszona.
OO idzie o krok dalej: np. W przypadku bibliotek możesz zdefiniować interfejs dla rzeczy, które jeszcze nie istnieją , i napisać kod, który będzie działał z tym interfejsem. Użytkownicy mogą pisać klasy implementujące interfejs i korzystać z usług udostępnianych przez bibliotekę. Pozwala to na pewien stopień elastyczności, który nie jest możliwy w przypadku programowania proceduralnego.
źródło
SetLocation
Można użyć do przesunięcia aMonster
, a doSetPosition
przesunięcia aPopupWindow
, iMove
można użyć do regulacji pozycji zDisplayCursor
). Próbowanie znaleźć właściwą metodę „ruchu” ...MyMonstor->
edytor pokazuje tylko listę metod, które można zastosować do rzeczy typuMonster
. Jeśli istnieje wiele tuzinów różnych rzeczy, z których każda obsługuje około tuzina operacji, zmniejszenie bałaganu na listach metod o 90% może znacznie zmniejszyć wydajność.it
typuSuperFancyWhizBang
, wywołanie jednej zSuperFancyWhizBang
metodit
nie wymaga zapisania typuSuperFancyWhizBang
; powiedzenieit.woozle()
sprawi, że kompilator automatycznie wyszukawoozle
wewnątrzSuperFancyWhizBang
.Istnieje sposób na zrobienie wszystkiego za pomocą maszyny Turinga lub co najmniej w języku asemblera dla kodu maszynowego, do którego ostatecznie skompiluje program C lub C ++.
Różnica nie polega więc na tym, co może zrobić kod, ale na tym, co ludzie mogą zrobić.
Ludzie popełniają błędy. Wiele.
OOP wprowadza paradygmat i składnię, która pomaga zmniejszyć rozmiar i gęstość prawdopodobieństwa przestrzeni możliwych błędów kodowania przez człowieka. Czasami czyniąc błąd niezgodnym z prawem dla określonej klasy obiektu danych (np. Nie jest to metoda zadeklarowana dla tego obiektu). Czasami popełniając błąd bardziej gadatliwie lub dziwnie wyglądając stylistycznie w porównaniu do kanonicznego użycia języka. Czasami wymagając interfejsu o znacznie mniej możliwych niespójnych lub zaplątanych zastosowaniach (publicznych lub prywatnych). itp.
Im większy projekt, tym większe prawdopodobieństwo błędów. Na który nowy program kodujący może nie być narażony, jeśli ma do czynienia z małymi programami. Zatem potencjalne zagadnienie, dlaczego OOP jest cenne.
źródło
Twoje pytanie wydaje się bardziej na temat celu OOP niż różnicy. Koncepcja w twoim poście to Encapsulation; i istnieje enkapsulacja wspierająca ZMIANĘ. Kiedy inne klasy uzyskują dostęp do twoich wewnętrznych elementów, trudno jest je modyfikować bez ich łamania. W OOP udostępniasz interfejs (członkowie publiczni), przez który pozwalasz innym klasom na interakcję z twoim, a także ukrywasz swoje elementy wewnętrzne, aby można je było bezpiecznie zmienić.
źródło
Mam nadzieję, że nigdy nie chcesz więcej niż jednego ciągu w swojej aplikacji. Mam również nadzieję, że zmienne lokalne będą się utrzymywać między wywołaniami funkcji. Te rzeczy mogą być takie same pod względem dostępności, ale pod względem żywotności i innych zastosowań? Nie są absolutnie takie same.
źródło
Jak wielu mówi, każdy program po skompilowaniu jest zamieniany na kod binarny, a ponieważ ciąg binarny może być użyty do przedstawienia liczby całkowitej, każdy program jest w końcu tylko liczbą. Jednak zdefiniowanie potrzebnej liczby może być dość trudne i dlatego pojawiły się języki programowania wysokiego poziomu. Języki programowania są tylko modelami kodu asemblera, który ostatecznie produkują. Chciałbym wyjaśnić wam różnicę między programowaniem proceduralnym a OO za pomocą tego bardzo ładnego artykułu na temat programowania kontekstowego http://www.jot.fm/issues/issue_2008_03/article4/
jak widać na tym obrazie, przedstawionym w artykule, programowanie proceduralne zapewnia tylko jeden wymiar do powiązania jednostki obliczeniowej z nazwą. W tym przypadku wywołania procedur lub nazwy są bezpośrednio mapowane na implementacje procedur. Na rysunku wywołanie m1 nie pozostawia innego wyboru niż wywołanie jedynej implementacji procedury m1.
Programowanie obiektowe dodaje kolejny wymiar rozpoznawania nazw do programowania proceduralnego. Oprócz nazwy metody lub procedury wysyłanie komunikatów uwzględnia odbiornik komunikatów podczas wyszukiwania metody. Na ryc. B widzimy dwie implementacje metody m1. Wybór odpowiedniej metody zależy nie tylko od nazwy wiadomości m1, ale także od odbiorcy faktycznej wiadomości, tutaj Ry.
To rzeczywiście umożliwia enkapsulację i modularyzację.
Rysunek c dotyczy wreszcie programowania zorientowanego na przedmiot, który rozszerza zorientowanie obiektowe o kolejny wymiar.
Mam nadzieję, że pomogło ci to myśleć w OOP z innej perspektywy.
źródło
(+1) Pytanie o coś, czego nie rozumiesz, jest dobre, nawet jeśli brzmi głupio.
Różnica polega na programowaniu obiektowym i klasowym. „Plain C”, działa z danymi i funkcjami. „C ++” dodaje pojęcia „obiekt i klasy” oraz kilka powiązanych pojęć wtórnych.
Jednak zalecam programistom nauczenie się „Plain C” przed „C ++”. Lub „Procedural Pascal” przed „Object Pascal”.
Wielu programistów uważa, że uczniowie powinni uczyć tylko jednej rzeczy.
Na przykład, starzy nauczyciele, którzy nie otrzymują OO i uczą tylko „Plain Structured C”.
Lub „hipsterowi” nauczyciele, którzy uczą tylko OO, ale nie „Plain C”, ponieważ „nie potrzebujesz go”. Lub oba, bez dbania o porządek nauczania.
Raczej uważam, że uczniowie powinni uczyć się zarówno „Structured Plain C”, jak i „Object Oriented C (C ++)”. Najpierw „Plain C” i „C ++”.
W prawdziwym świecie musisz nauczyć się obu paradygmatów (oraz innych paradygmatów, takich jak „funkcjonalny”).
Pomóc może myślenie o ustrukturyzowanych programach jako dużym, pojedynczym „obiekcie”.
Powinieneś także położyć nacisk na przestrzenie nazw („moduły”), w obu językach wielu nauczycieli po prostu to ignoruje, ale to ważne.
źródło
foo()
w C ++, może to być funkcja globalna, funkcja w bieżącej przestrzeni nazw, funkcja w przestrzeni nazw, której używaszusing
, metoda, metoda dziedziczona, a jeśli jest w wywołaniu funkcji: może to być przestrzeń nazw, która można rozwiązać za pomocą wyszukiwania nazw na podstawie argumentów, podobnie jest w przypadku Java i C #. W C może to być tylko funkcja statyczna w bieżącym pliku źródłowym lub jedna z nagłówka.Jednym słowem, zarządzanie projektami. Chodzi mi o to, że C ++ pomaga mi egzekwować zasady używania mojego kodu przez innych. Pracując nad projektem o wartości 5,5 miliona linii programowanie obiektowe jest bardzo pomocne. Kolejną zaletą jest kompilator, który zmusza mnie (i wszystkich innych) do przestrzegania pewnych zasad i wychwytywania drobnych błędów w czasie kompilacji. Są też wszystkie teoretyczne zalety, ale chciałem skupić się na codziennych praktycznych rzeczach. W końcu wszystko to kompiluje się w kod maszynowy.
źródło
Programowanie obiektowe to Programowanie proceduralne z ramkami.
W PP masz jedno pudełko, jeden stan, który staje się niewiarygodnie duży w miarę rozwoju projektu, powodując pojawienie się efektów ubocznych za każdym razem, gdy zapomnisz trochę tego dużego stanu.
W OO masz wiele skrzynek, wiele stanów, a wraz z rozwojem projektu, pudełka trochę rosną, a liczba pudeł rośnie.
Łatwiej jest patrzeć na mniejsze pudełka, łatwo mieć iluzję całego obrazu, ale w rzeczywistości prawie niemożliwe, ponieważ patrząc na klasy i interfejsy ukrywa się szczegóły implementacji, które mogą mieć ważne konsekwencje.
W programowaniu funkcjonalnym masz wiele pól funkcyjnych i decydujesz, że każda funkcja ma jeden wpis (parametry) i jedno wyjście (powrót), bez żadnego innego dostępu do kontekstu zewnętrznego.
Ponieważ nie ma stanu i efektów ubocznych (zgodnie z projektem), możesz bezpiecznie analizować dowolną funkcję oddzielnie od całości i wiedzieć w 100%, jak będzie się zachowywać w każdych okolicznościach.
Ponieważ kodujesz kodowanie według jednostek logicznych reprezentujących akcje, możliwe jest również posiadanie tylko jednego pola na typowe działanie.
Spowoduje to zmniejszenie kodu dowolnego projektu na dużą skalę w porównaniu do OOP, który promuje ukrywanie wielu funkcji analogowych w całej bazie kodu w różnych klasach.
To również znacznie przekroczy PP, ponieważ możesz rozwijać projekt znacznie dłużej, ponieważ nie ma już stanu XXXXXXXL do śledzenia.
Podsumowując, PP jest prawdopodobnie najprostszym sposobem podejścia do prostego programu, a FP jest prawdopodobnie najprostszym sposobem podejścia do złożonego.
Jeśli weźmiesz pod uwagę cel ujednolicenia wszystkich baz kodu i poprawy ponownego wykorzystania kodu, zawsze należy stosować FP, ponieważ jest to jedyny paradygmat, który ma sens na bardzo dużą skalę, a także jedyny paradygmat, który ma 100% możliwości ponownego użycia (możesz po prostu skopiuj wklej funkcję i użyj jej gdzie indziej, bez żadnych narzutów).
Otrzymujesz w 100% niezawodne testy jednostkowe za darmo.
I nie musisz pisać „prywatny statyczny finał genialnego genialnego niesamowitego ciągu_1”.
I dostajesz równoległość za darmo.
źródło
Prosta różnica w jednym zdaniu polega na tym, że C ++ to C z klasami (choć teraz jest o wiele więcej). Nie rozumiem, dlaczego nie chcesz poznać różnic między dwoma, czytając świetny artykuł na temat C ++ na Wikipedii .... .Ten artykuł bardzo ci pomoże: - C ++ (Wikipedia)
Pomocne będzie także wyszukiwanie go w Google. Poproszenie przypadkowych osób o wyjaśnienie tego może być trudne. IMHO lepiej rozumie się czytając niż pytając kogoś
źródło