Programista C/C++ ?

0
Świętowit napisał(a)
Will napisał(a)

Znów pokazujesz, że umiejętność czytania ze zrozumieniem nie jest twoją mocną stroną.

Chyba Ty, wyraźnie zaznaczyłem, że chodzi mi przede wszystkim o stropień trudności języka. C++ jest najtrudniejszym, najbardziej niespójnym i niedorobionym językiem z mainstreamu, nie spotkałem w życiu programisty, który twierdziłby, że język nie tworzy trudności i jest prosty do opanowania (jak większość innych).

Will napisał(a)

"są językami szkoleniowymi"- miałem na myśli tylko początki nauki programowania gdzie ludzie co raz częściej wybierają c#/jave i nie muszą babrać się ze wskaźnikami, leakami, pisać własne alokatory itd.

Twierdzisz, że w językach z GC nie mam memleaków? Pomijając kwestie interakcji z rzeczami, których czas życia musi być ściśle określony to pozostaje problem chociażby ze zwalnianiem referencji, żeby jakieś trupy z odwołaniami nie leżały na stercie do końca programu.

Życia uczy (ANSI) C, nie C++. C++ uczy patologii, pijaństwa i tanich dziwek...

A ja uważam, że jest całkiem prosty ale taka już moja opinia. Zaczynałem od c++ i zawsze fajnie się z nim pracowało. Daj początkującemu "bawić" się z memleakami w c++ i c# a zobaczysz co miałem na myśli. Nie twierdze, że c++ jest we wszystkim lepszym wyborem, poruszałem głównie kwestie gamedev'u, sam często pisze w c# różne przydatne tool'e i jestem bardzo z tego wyboru zadowolony.

0

A powiedz mi w końcu Will Ty pracujesz czy nadal się uczysz??? Bo chyba cieżko porównać pisanie w pracy do pisania na uczelni...

0

może być tak, że gadamy teraz z chłopakiem z 3 roku który chwali C++ a tak na prawdę go nie zna... Sam byłem oczarowany , dopóki nie poznałem lepiej np. C#. Nie jestem jakimś wybitnym programistom, własciwie jestem na poczatku mojej drogi ale wiem, że na C# jest przyjaźniejszy dla programisty i gdybym miał wybór pisac w C++ a w C# to wybrał bym to drugie...

0

Zastanawiam się tylko nad pojęciem "pseudo obiektowości", czego brakuje mechanizmom obiektowym c++'a, aby można było mówić o obiektowej stronie tego języka, a nie "pseudo obiektowej"

Brak pełnej enkapsulacji. Kod kliencki wymaga znajomości sekcji private klas, z których korzysta, i nic na to nie poradzisz (to jest bardzo wbrew OOP). Co powoduje, że każde grzebanie w bebechach prywatnych klasy pociąga rekompilację dużej ilości kodu. Tak samo zrobienie obiektowego systemu wtyczek jest dużo trudniejsze niż w takim C# czy Javie (a przynajmniej utrzymujących kompatybilność wsteczną na poziomie binarnym - bo tam wystarczy jedynie pilnować niezmienności interfejsu publicznego, a nie całości).

0

Akurat na to jest półśrodek w postaci PIMPL... który łatwo zniweczyć używając deklaracji przyjaźni, jednej z najbardziej spieprzonych i niebezpiecznych konstrukcji w C++.

0

Takie wynalazki jak PIMPL raczej właśnie świadczą o słabości języka niż o jego sile.
Nikt nie twierdzi, że nie ma OOP w C++, tyle że jest takie dosyć... toporne.

A co do przyjaźni, to faktycznie. Tak jakby nie mogli zrobić po ludzku pakietów i czegoś w stylu package-private (swoją drogą później dodali coś na kształt pakietów - tj. przestrzenie nazw).

0

Oczywiście uważam PIMPL i kilka innych koniecznych dla C++ zabiegów za totalny idiotyzm i tworzenie nadmiarowego kodu.

Friend jest cholernie niebezpieczne bo w 'niektórych' wypadkach doświadczony programista może dostarczyć własny twór, który będzie zaprzyjaźniony i jednocześnie będzie nie powodował konfliktów przy linkowaniu - źle użyte friend to otwarcie klasy na public, kwestia zrobić wrapper, który pozwoli na ładne grzebanie...

Przestrzenie nazw miały niby separować symbole, unikać zaśmiecania globalnej... ale wyszło jak wszystko inne w C++. Sposób dopasowania nazw jest spieprzony, chodziło o to żeby dało się operatory umieszczać w klasach/przestrzeniach i się nie 'gubiły'. W efekcie można chociażby takie cuda robić (nazw nawet nie chce mi się zmieniać, jeden z wielu cudów pisanych bo się wybitnie nudziłem nie mogąc spać):

namespace dupka {
  class cipka {};
  class pipka {};

  void zuuoo(dupka::cipka) {}
}

namespace {
  void zuuoo(dupka::pipka) {}
}

int main() {
  dupka::cipka cipka;
  dupka::pipka pipka;

  zuuoo(cipka);
  zuuoo(pipka);
}

Zachowuje się jakby w globalnej przestrzeni była przeciążona funkcja, prawda? Taki to 'wygodny' sposób dopasowywania nazw, który może czasem do konfliktów prowadzić.

Model 'modularyzacji' w C++ pochodzi z wczesnego C (paczki z tablicami symboli, generowane z jednostek translacji) i jest tak naturalny dla języków wysokiego poziomu jak dendrofilia dla statystycznego człowieka. O tyle śmieszne, że dałoby się sensowny mechanizm paczek dodać bez zrywania kompatybilności ze starym kodem... No ale święta dla C++ wsteczna kompatybilność jest rozumiana przez robienie rozszerzeń na stare błędy często wprowadzając jeszcze większy chaos.

Mój ulubiony efekt krótkowzrocznego łatania i dodawania pseudoobobiektowości do typów prostych:

typedef void v01d; // 'void' jest słowem kluczowym i nie może być użyte w kontekście funkcji/metod
v01d().v01d::~v01d();

Tak, 'tworzenie' tymczasowej wartości typu void i wywołanie jej destruktora... z całkowitym błogosławieństwem standardu języka (nawet i nadchodzącego, jak Coyote Forever, C++0x). Jeżeli jakiś kompilator tego nie kompiluje to znaczy, że to g**no, nie kompilator zgodny z ANSI/ISO C++.

0

Za poważną wadę C i C++ uważam brak jakiegokolwiek mechanizmu modułów (unitów).
Inkludowanie na żywca drugiego pliku źródłowego (zwanego "nagłówkowym"), który oprócz prototypów funkcji może zawierać dowolne śmieci to jakiś niepoważny hack.
Przez to też kompilator nie ma pojęcia które pliki kompilować, których nie, i trzeba się posiłkować zewnętrznymi narzędziami typu make.

0
Azarien napisał(a)

Za poważną wadę C i C++ uważam brak jakiegokolwiek mechanizmu modułów (unitów).
Inkludowanie na żywca drugiego pliku źródłowego (zwanego "nagłówkowym"), który oprócz prototypów funkcji może zawierać dowolne śmieci to jakiś niepoważny hack.
Przez to też kompilator nie ma pojęcia które pliki kompilować, których nie, i trzeba się posiłkować zewnętrznymi narzędziami typu make.

a preprocesor i kompilacja warunkowa czy o jeszcze coś ci chodzi ? Fakt, że kolega miał kłopoty, jednak to przez niedoświadczenie - załączył we wszystkich plikach swoją klase i miał xxx redefinicji ;p

0

a preprocesor i kompilacja warunkowa czy o jeszcze coś ci chodzi ?

Preprocesor też. Jaką masz pewność, czy dołączany nagłówek nie zawiera jakiegoś "#define true false"? Jakieś niespodziewane makro czy redefinicja, powodujące problemy bardzo trudne do wykrycia.

0

C++ złe, C++ niedobre. Tylko co to zmienia i tak w tym gówienku trzeba się taplać, bo w niektórych dziedzinach nie ma go czym zastąpić. Już nie wspomnę o tym, że na każdy popularny język się najeżdza bo nowiutki języki X robi to 10x ładniej i jest mniej bałaganiarski.

0
studenciak napisał(a)

C++ złe, C++ niedobre. Tylko co to zmienia i tak w tym gówienku trzeba się taplać, bo w niektórych dziedzinach nie ma go czym zastąpić.

Zastąpić to zwykle jest czym, tylko nikt nie będzie pisał wszystkiego od nowa tylko po to, żeby nie było w C++. Z tego samego powodu w bankowości tak długo utrzymał się Cobol.

Już nie wspomnę o tym, że na każdy popularny język się najeżdza bo nowiutki języki X robi to 10x ładniej i jest mniej bałaganiarski.

Do czasu. Bo po paru latach zgodnie z modą do nowiutkiego języka dodadzą krotki, srotki, wyrażenia lambda, theta i ypsilon, i robi się z tego to co się zrobiło z C++.

0

Do czasu. Bo po paru latach zgodnie z modą do nowiutkiego języka dodadzą krotki, srotki, wyrażenia lambda, theta i ypsilon, i robi się z tego to co się zrobiło z C++.

Trochę tak, a trochę nie. Zależy od podejścia twórców.
C++ jest brzydki nie dlatego, że ewoluował, tylko że w trakcie tej ewolucji za wszelką cenę chciano uzyskać kompatybilność wsteczną z C, oraz dodatkowo założono, że nowe udogodnienia nie mogą wprowadzać narzutu czasu wykonania względem tego co było w C.

Tak samo w Javie zrobili genericsy do połowy dlatego, że za wszelką cenę nie chcieli zrywać kompatybilności. C#, który był wtedy 20x mniej popularny mógł sobie na to pozwolić i ma to rozwiązane lepiej. Ot, za utrzymanie popularności płaci się cenę.

0
Krolik napisał(a)

Tak samo w Javie zrobili genericsy do połowy dlatego, że za wszelką cenę nie chcieli zrywać kompatybilności. C#, który był wtedy 20x mniej popularny mógł sobie na to pozwolić i ma to rozwiązane lepiej. Ot, za utrzymanie popularności płaci się cenę.

W jaki sposób wprowadzenie generyków w C# zerwało z czymkolwiek kompatybilność?
Bo ja nie widzę żadnych skutków ubocznych. Co się kompilowało, to się kompiluje.

0

Tak, ale wprowadzono równoległą bibliotekę generyczną do kontenerów. W Javie chciano tego uniknąć, aby nie zaśmiecać biblioteki standardowej. Można było uczynić istniejące klasy generycznymi, ale wtedy kod, który kiedyś się kompilował, przestałby się kompilować w niektórych sytuacjach. Myślę że jednak podejście do tej sprawy w C# było lepsze na dłuższą metę niż w Javie (choć i tak jedne i drugie genericsy są IMHO słabe, choćby dlatego, że List[A] nie dziedziczy po List[B], jeśli A jest podklasą B, podobnie jak w C++).

Niemniej twórcy Pythona i Scali mają jaja i jak się coś słabo w języku sprawdza albo jest zbyt zawikłane, to nie mają skrupułów wywalić albo całkowicie przeprojektować. I rzeczy oznaczane jako deprecated też wypadają po dwóch wersjach w przeciwieństwie do Javy i C#. Jeśli będą tak trzymać, to język nigdy nie stanie się zaśmiecony i może bardzo długo być elegancki i spójny.

0

(choć i tak jedne i drugie genericsy są IMHO słabe, choćby dlatego, że List[A] nie dziedziczy po List[B], jeśli A jest podklasą B, podobnie jak w C++).

Rozwiązane w .NET 4.0 (podobno)

0

Tja, tylko w Scali było bodaj od zawsze i działa lepiej, jest zwyczajnie bardziej naturalne. Inferencja i dosyć inteligentny polimorfizm jednak swoje robią. Zaraz pewnie przyjdzie Krolik i zrobi o tym ładny wykład.

0

A właśnie że nie, jeszcze okrzykną mnie fanbojem Scali. :P
Każdy może doczytać sobie sam, spróbować jednego i drugiego i zobaczy, gdzie kowariancja sprawdza się lepiej (hint: kolekcje niemutowalne).

0

Ja, jak zwykle zresztą w takich wątkach, optuję za rozwojem D, który z kompilowalnych do kodu maszynowego ma wg mnie największe szanse. Największą zaletą w stosunku do C++ jest zerwanie pseudokompatybilności z kodem C. D jest zgodnie jedynie na poziomie binarnym.
Zalety D IMHO:

  • ludzkie moduły (koniec z preprocesorem i plikami nagłówkowymi, mimo iż taka możliwość również jest, ale rzadko stosowana, przyda się jedynie przy komercyjnych bibliotekach)
  • statyczne ify i zastępujące preprocesor
  • twarde typedefy, a zamiast miękkich typedefów aliasy
  • możliwość leniwej ewaluacji
  • wbudowane delegaty
  • proste i logiczne definicje zmiennych ([typ] [nazwa];)
  • przejrzyste definicje wskaźników na funkcje
  • wbudowana lambda
  • brak wielokrotnego dziedziczenia na rzecz interface'ów
  • wbudowane hash tablice i tablice dynamiczne
  • wbudowana obsługa liczb zespolonych oraz urojonych
  • wbudowany GC

Problemem jest jedynie dość mała ilość narzędzi (patrząc na rok wydania wersji D2.0, która zmieniła prawie wszystko [2008] to nie ma co się dziwić) i przymierająca trochę społeczność. Jednak mam nadzieję, że niedługo wszystko się rozbudzi.
Innym istotniejszym problemem jest jeszcze nieustandaryzowana biblioteka standardowa. W "oryginalnym" kompilatorze od Digital Mars jest dostarczany Phobos, który nie jest najwyższych lotów, ale działa z D2.0, ale większość dotychczasowych projektów w D1.0 korzysta z Tango, które jest bardziej obiektowe i podobne do Javowego. Osobiście chciał bym mieć port Tango (pod inną nazwą, bo ta jest fatalna) do D2.0, nad którym częściowo sam pracuję.

0

D był, jest i będzie wyłącznie ciekawostką. Śmieszna próba zrobienia czegoś nowoczesnego z C, już takie były. Za nim dosłownie nikt nie stoi, nikt go nie używa, autorzy mieli nadzieję, że kogoś nim zainteresują, tyle. Taki Google Go ma ledwo kilka miesięcy (i jest paskudny) a już jest zdecydowanie popularniejszy od D...

0

Może i jest ciekawostką, ale parę fajnych rzeczy wprowadza. Np. wreszcie sensownie zrobione const. Wiem, że goście od Scali mocno się temu przyglądają (i podejrzewam, że wrzucą do Scali wcześniej czy później coś równie dobrego, albo mocniejszego). Zapewne twórcy innych nowych języków się też wzorują.

Nie można osądzać wagi czegoś tylko po popularności. Czy ktoś pamięta system baz danych System-R? Prócz starych profesorów i pracowników IBMa pewnie prawie nikt. A był to system rewolucyjny, z którego 99% obecnych RDBMSów czerpie pełnymi garściami - łącznie z klasycznym algorytmem optymalizacji zapytań opracowanym w 1979 r. (sic!), który bez większych zmian pozostał do dzisiaj we wszystkim prócz MySQLa (który, siedzi w epoce przed 1979 r, pod tym względem).

0

A ktokolwiek pamięta jeszcze język B?

0

A co to za język? Mniemam, że mniej popularny od Brainfucka :)

0
MSM napisał(a)

(choć i tak jedne i drugie genericsy są IMHO słabe, choćby dlatego, że List[A] nie dziedziczy po List[B], jeśli A jest podklasą B, podobnie jak w C++).

Rozwiązane w .NET 4.0 (podobno)

W Javie to jest już od kliku lat - od chwili wprowadzenia genericów, czyli od wersji 5.
W poniższym przykładzie List< B> dziedziczy po List<? extends A>.

public class GenericsInheritanceTest {
	static class A {public void foo() {System.out.println("A");}};
	static class B extends A {public void foo() {System.out.println("B");}};

	public static void main(String[] args) {
		List<B> listB = new ArrayList<B>();
		listB.add(new B());
		List<? extends A> list = listB;
		for(A a: list){
			a.foo();
		}
	}
}

</b></b></b>

0

Jelsi genericsy w Javie stawiasz jako wzor, to skoncz.

0

O, uczciwy programista Javy.

0

Nieco doswiadczony ;-)

0

Kowariancja typów generycznych definiowana w miejscu użycia (aka genericsy w Javie) to nie to samo, co kowariancja określona w miejscu definicji klasy.

Możesz tak?

class Node[+T](val value: T)

Teraz Node[A] mogę użyć wszędzie tam, gdzie jest spodziewane Node[B] jeśli A dziedziczy po B. Niezależnie od tego, jak został napisany kod, który operuje na tych obiektach.

Nie przychodzi mi do głowy na razie prosty przykład, gdzie takie rozwiązanie jest lepsze niż kowariancja w miejscu użycia, bo właśnie kowariancja w miejscu użycia to takie coś, co ładnie wygląda w przykładach i na slajdach. Natomiast w złożonym kodzie zauważyłem, że bardzo łatwo prowadzi do dziwolągów, zwłaszcza przy zagnieżdżaniu wielu poziomów genericsów. A globalne założenie, że np. List[Int] jest podklasą List[Any] upraszcza bardzo wiele rzeczy.

BTW: Jak będzie to rozwiązane w C# 4.0? Oba sposoby definiowania kowariancji, czy tylko taki jak w Javie?

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