Kumpi kieli soveltuisi teidän mielestä paremmin peli ohjelmointiin c vai c++? mitä etuja ja haittoja kielissä on ohjelmoidessa pelejä? onko aiheesta juttua jossakin muualla, itse yritin etsiä mutta en paljoa löytännyt. ja onko jossakin hyviä oppaita pelien ohjelmointiin kyseisellä kielellä?
Kumpikin on aikalailla yhtä hyviä, ainut selkeä ero on se että C++:lla voi käyttää olioita... Mutta minusta henkilökohtaisesti C++ on selkeämpi ja muutenkin mukavempi käyttää kuin C. Mutta lopputuloksen voi kummallakin saada yhtä hyvän.
Suosittelen kuitenkin C++:aa.
Edit: Miksi käyttää "vanhentunutta" kieltä kun sen tilalle on viitsitty kehitellä uusi versio...
Tärkein asia on, että
Tumpelo kirjoitti:
lopputuloksen voi kummallakin saada yhtä hyvän.
Minä varmaan käyttäisin C:tä, kun osaan sitä paremmin.
murinaa. C++ ei ole C:n uusi versio. C++ ei edes ole C:n superset (enää).
C:llä saa tehtyä nopeampia ja pienempiä ohjelmia kuin C++:lla. C++:ssa on toisaalta joitakin monimutkaisten pelien kannalta korvaamattomia ominaisuuksia, kuten mallit. Yksinkertainen peli kannattaa varmaan tehdä C:llä, jollei kaipaa C++:n kätevyyksiä, kuten:
for(int i=1, b=1; i<max; i++) b *= i;
Riippuu siitä, kuinka paljon on käyttöä C++:n lisäominaisuuksille. Jos on esim. vasta aloittelija, niin silloin on parempi käyttää C:tä ja ottaa C++:n ominaisuuksia käyttöön sitä mukaa, kun tarvitsee.
Tuosta nimenomaisesta C++:n "kätevyydestä" on ainakin minulle ollut enemmän harmia kuin hyötyä. Sman tien ne muuttujat määrittelee kerralla alussa.
Käytä sitä mitä osaat, tai jos osaat kumpaakin niin käytä C++:aa :)
C++:ssa on mukavia ominaisuuksia jotka auttavat pienissäkin projekteissa, esim. standardikirjaston stringit ja vectorit. C++ on aloittelijallekin mukavampi kun ei tarvitse heti opetella pointtereiden ja taulukkojen kanssa pelaamista. Oliot, nimiavaruudet ja muut auttavat sitten kun peliprojekti kasvaa. C++:lla voi myös tehdä aivan yhtä nopeita ohjelmia kuin C:lläkin, ja kokokin on sama jos standardikirjastoa ei lasketa.
Minusta tuo selkeyttää että voi määrittää muuttujat vasta siinä vaiheessa kun niitä käytetään. Eihän sitä tietenkään pakko ole käyttää, ja eikös C99:ssä ole tuo ominaisuus myös?
Olennaistahan ei tietenkään ole se, mitä ominaisuuksia käyttää ja mitä ei, vaan se, saako ohjelman kääntyymään C:nä.
edit: Muutenhan kannattaa tietenkin käyttää C++:ssaa :), vrt:
int *cpp = new int[10]; int *c = (int*)malloc(10*sizeof(int));
Onko jossain päin sitten hyvää materiaalia c++:salla pelien tekemiseen? kuinka paljon win c++ ja linuxin c++ eroaa toisistaan? Mitä "hyviä" ominaisuuksia sitten siinä c++:sassa on?
Käytä järjestelmäriippumattomia rajapintoja, jotta saat portattua pelisi monelle alustalle. Windows ja linux omaavat saman standardin c++:n (windowsissa ei oletuksena). Jotkin lisäkirjastot eivät aina toimi molemmissa (tarkoititko tätä?).
os kirjoitti:
edit: Muutenhan kannattaa tietenkin käyttää C++:ssaa :), vrt:
int *cpp = new int[10]; int *c = (int*)malloc(10*sizeof(int));
Ai jaa?
vrt.
// C++-resize int *cpp = new int[vanha_koko]; int *tmp = new int[uusi_koko]; memcpy(tmp, cpp, sizeof(int) * (vanha_koko < uusi_koko ? vanha_koko : uusi_koko)); delete [] cpp; cpp = tmp; // C-resize int *c = (int *) malloc(10 * sizeof(int)); c = (int *) realloc(c, uusi_koko);
No joo. C++:ssa ei pitäisi joutua muuttamaan kokoa; jos sitä tarvitsee, niin pitäisi käyttää std::vector-luokkaa. Mutta periaatteessa noin rumasti.
os kirjoitti:
C:llä saa tehtyä nopeampia ja pienempiä ohjelmia kuin C++:lla.
Jos otan C-koodin ja käännän sen C++-kääntäjällä, niin tuloksena ei taida olla yhtään sen hitaampi ja suurempi ohjelma kuin C-kääntäjälläkään.
C++:n I/O-kirjasto tuppaa kyllä usein olemaan C:n vastinetta hitaampi. Toisaalta esimerkiksi C++:n sort
toimii usein nopeammin kuin C:n qsort
, vaikka samaa algoritmia käyttävätkin.
Kyllä minä vaan käyttäisin C++:aa, on se niin paljon kätevämpi.
koo kirjoitti:
Kyllä minä vaan käyttäisin C++:aa, on se niin paljon kätevämpi.
Samaa mieltä. Ohjelmointi puhtaalla C:llä tuntuu jotenkin riisutulta ja rajoittuneelta (siis tuntuu, en ole mitään ihmeempä puhtaalla C:llä tehnyt). Olit, vektorit, stringit jne. ja elämä on paljon helpompaa :)
Tutustuin tässä olio-ohjelmointiin ja kyllä se on erittäin hyödyllinen ominaisuus. Koodi on selkeämpää, järeämpää, luotettavampaa ja uudistettavampaa. Tästä huvista C:n käyttäjät jäävät paitsi. :P
Tutustuin itsekkin olio-ohjelmointiin ja c++:saan peliohjelmoinnissa ja se on kylläkin todella paljon selkeämpää minusta kuin normaali c:n käyttö, kiitos vastanneille.
Cc kirjoitti:
Tutustuin itsekkin olio-ohjelmointiin ja c++:saan...
:-) Syvällisempään perehtymiseen saattaakin sitten mennä vuosia... Mutta on se parempi harrastus kuin mummojen potkiminen, joten onnea vaan matkaan. Tai mistäs minä tiedän, kun en ole käynyt potkimassa. :-P
Metabolix kirjoitti:
No joo. C++:ssa ei pitäisi joutua muuttamaan kokoa; jos sitä tarvitsee, niin pitäisi käyttää std::vector-luokkaa. Mutta periaatteessa noin rumasti.
Nämä esimerkit ovat vähän huonoja, kun taulukkovarauksia ei kunnon C++-koodissa pahemmin tehdä. Kyllä C++:ssa saa käyttää kirjastoluokkia yhtä lailla kuin C:ssä kirjastofunktioita. Rumuus on tietenkin katsojan silmässä, mutta vertaillaanpa nyt vielä niin, että asiat tehdään oikein:
// C++-resize std::vector<int> cpp(vanha_koko); cpp.resize(uusi_koko); // C-resize int *c = malloc(vanha_koko * sizeof(int)); if (c == NULL) abort(); int *tmp = realloc(c, uusi_koko * sizeof(int)); if (tmp == NULL) abort(); c = tmp; free(c);
Kommenttina vielä, että C:ssä muistinvarausfunktioiden paluuarvojen castaus on turhaa ja toteutuksesta riippuen jopa vaarallista. C++:ssa nimenomaan malloc
:in ja kumppaneiden paluuarvojen castaus on pakollista, mikä onkin pikku vihje olla käyttämättä noita C-funktioita C++-koodissa.
Tossa C++ koodissa taas ei ole virhetarkistusta
ja reallocia varten ei tarvi väliaikaista muuttujaa. Eikä oikein saisi muuttujia esitellä siellä täällä.
C++ tapa on kieltämättä vähän siistimpi, varsinkin jos lohkossa on monta samaa poikkeusta heittävää operaattoria, koska siihen riittää vain yksi catch. Toisaalta mikään ei estä tehdä 'xmallocia' ja 'xreallocia' (glibc:n manualissa taitaa olla peräti esimerkki), jotka hoitavat tarkistuksen. C99:ssä on varsinkin inline funktiot, joten ei tulisi edes overheadia siitä.
C:ssä silti kannattaa tehdä kastaus, ja se on hyvä tapa, koska kääntääjän saa herjäämään vääräntyyppisistä sijotteluista.
Turhaa näiden kielien eroista kiistää. Toinen tykkää toisesta ja toinen toisesta.
// C++ try { std::vector<int> cpp(vanha_koko); cpp.resize(uusi_koko); } catch (bad_alloc) ( abort(); } cpp.clear(); /* C */ int *c = (int) malloc(vanha_koko * sizeof(int)); if (!c) abort(); c = (int) realloc(c, uusikoko * sizeof(int)); if (!c) abort(); free(c);
edit: oletan että vector heittäisi bad_allocia, vielä kun löytyis jostain dokua
phadej kirjoitti:
Tossa C++ koodissa taas ei ole virhetarkistusta
ja reallocia varten ei tarvi väliaikaista muuttujaa. Eikä oikein saisi muuttujia esitellä siellä täällä.
Kyllä siinä on tuota C:tä vastaava tarkastus: jos vector
heittää bad_alloc
:in tai muun poikkeuksen, ohjelma kuolee hallitusti. catch
ja abort
ovat tarpeettomia.
Väliaikainen muuttuja tarvitaan, jos realloc
:in epäonnistumisen jälkeen aiotaan tehdä yhtään mitään muuta, kuin abort
tms., muuten alkuperäinen osoitin menetetään. Mutta totta, ei se tässä ihan pakollinen olisi.
Viimeisin C-standardi taitaa antaa esitellä muuttujia muuallakin kuin lohkojen alussa. On ihan järkevää esitellä muuttujat mahdollisimman lähellä sitä paikkaa, jossa niitä käytetään.
phadej kirjoitti:
C:ssä silti kannattaa tehdä kastaus, ja se on hyvä tapa, koska kääntääjän saa herjäämään vääräntyyppisistä sijotteluista.
Joo, jos itse castaa väärään tyyppiin. Muissa, tavallisemmissa virheissä se nimenomaan tukkii kääntäjän turvan, sillä vaikka kääntäjällä olisikin funktioesittelyjen yms. puolesta jotakin valittamista, casti kertoo kääntäjälle, että ok, koodaaja näyttää tietävän paremmin. Siis: huono tapa.
Vaaralliseksi castaus muuttuu vaikkapa tilanteessa, jossa kääntäjä ikänsä tai jonkun yhteensopivuusmoodin takia sallii esittelemättömien funktioiden kutsumisen. Leikitään vielä, että int
on 32-bittinen ja osoittimet ovat 64-bittisiä. Mitä tapahtuukaan tässä, jos headeria <stdlib.h> ei ole includoitu?
int *pi = (int *) malloc(sizeof(int));
Funktiota ei ole esitelty, joten sen paluuarvo oletetaan int
:ksi. Casti muuttaa sitten tuon 32-bittisen arvon 64-bittiseksi osoittimeksi. Funktio oli kuitenkin alun alkaen tarjoamassa 64-bittistä void
-pointteria, joka sellaisenaan castautuu C:ssä tarvittaessa int
-pointteriksi. Nyt vaan puolet osoittimen sisällöstä voi korvautua hötöllä!
std::vector
-muuttujaa ei tarvitse clear
ata käytön jälkeen. Sen sisältö tuhotaan samalla kun muuttuja itsessään lakkaa olemasta esimerkiksi lohkosta poistumisen takia.
vector
ei oikeastaan itse nakkele exceptioneita, mutta esimerkiksi std::bad_alloc
voi tulla, kun vector
allokoi tilaa alkioilleen. Muitakin poikkeuksia voi tulla, vaikkapa kun alkio on jokin olio, jonka kopioiva konstruktori päättää sellaisia viskoa.
Kuolee hallitusti, mutta ei kerro miksi teki niin.
Ja noita yhteensopivuusmoodia ei oikein käytetä. GCC:ssä on hyvä käyttää -Wall ja -Wstrict-prototypes vipuja niin ei tule yllätyksiä.
Ja kastaamisia on erillaisia,
// imo hyvä int *ptr = (int*) malloc(sizeof(int) * 10); // en tiedä miten tämän tekisi siististi ilman kastaamista int i = 123; int j = 10; double d = (double)i / j; // huono (esimerkkikin), mutta miksi kukaan tekisi niin int n(int m) { return -m; } long l[10]; n ((int)l);
Tämä menee vähän jossitteluksi:
Jos reallocin epäonnistumiseen jälkein ajotaan tehdä yhtään mitään niin tarvisi väliaikainen muuttujaa, mutta sitten tarvisi C++:n try-catch:kin. Toisaalta se bad_allocin kiiniottajakaan tiedä containerista mitään, ellei se on esitetty jo ennen.
Niin kuin itse sanoit muitakin poikkeuksia voi tulla. ja niihin pitää varautua. vector on kiva, oikein käytettynä, mutta jos siihen tunkee jonkin oman classin joka on toteutettu huonosti (esim ei oo varautunut exceptioneihin) niin muistia voi vuotaa tai muuten sekoittaa containerin.
Laitoin ton clearin ihan sen takia, että jos siinä vaiheessa muistia on vapautettava.
Vektoria ja mallocia toisaalta on myös 'väärin' vertaila,
vectori on paljon korkeakielisempi rakenne (se käyttää itse allocia tai jopa mallocia jos jostain syystä sitä halua).
voihan olla noikin:
// Vector on siis typedef pointeriin jonkinnäköiseen structiin. Vector v; v = vector_new(vanha_koko); vector_resize(v, uusi_koko); vector_delete(v); /* funkkarit voivat sitten epäonnistuessaan jo kutsua jonkinnäköisen errorhandlerin, joka voi sitten yrittää korjata tilannetta tai yksinkertaisesti lopettaa ohjelman. */
C:ssä voikin siis tehdä jonkinlaisen vektorityypisen rakenteen, jos sellaista haluaa. C++ tarjoa sen jo vakiona ja aika hyvin toteutettuna.
ainiin, kauneus on katsojan silmissä.
selvitykseksi: en itse osaa sanoa kummasta kielestä pidän enemmän.
C++:ssa ottaa päähän g++ (hitaus) eniten, toivottovasti siihen tulee parannusta. Ja sitten alkuun se oli aika valtava kieli. C:stäkään osaa kaikia juttuja, niinkuin todettu, oppimiseen menee vuosia.
edit: ainiin piti olla std::bad_alloc aikaisemmassa postissa. namespacet on toisaalta kivoja toisaalta rasittavia. kun ei viiti usingiakaan turhaan käyttää.
Ei se C:n abort
kaan pahemmin kerro, että mikä tuli.
Yhteensopivuusmoodeja ei kannattaisi käyttää, mutta maailmassa on ihmeen paljon esimerkiksi K&R-tyyppistäkin C-koodia eikä esittelemättömien funktioiden kutsuminen ole ollut laitonta ennen viimeisintä C-standardia.
Castaamista pitää käyttää vain kun se on ehdottomasti tarpeen, ei mitenkään rutiininomaisesti. Kielen omat säännöt ovat turvallisempia, kääntäjän varoitukset on syytä ottaa tosissaan eikä niitä kannata vaientaa omilla casteilla.
// tarpeeton ja jopa vaarallinen cast int *ptr = (int*) malloc(sizeof(int) * 10); // castiton vaihtoehto int i = 123; int j = 10; double d = i; d /= j; // Mahtava juttu, annetaan parametrina pointteri longiin, // kun funktio odottaa intiä! Castin takia kääntäjä päästää // koodin läpi, koska koodaaja selvästikin haluaa niin. int n(int m) { return -m; } long l[10]; n ((int)l);
phadej kirjoitti:
Jos reallocin epäonnistumiseen jälkein ajotaan tehdä yhtään mitään niin tarvisi väliaikainen muuttujaa, mutta sitten tarvisi C++:n try-catch:kin. Toisaalta se bad_allocin kiiniottajakaan tiedä containerista mitään, ellei se on esitetty jo ennen.
Tuo vector
on ihan samassa tilassa ja sen sisältö on sama kuin ennen exceptionia. try-catch
:llä voidaan kyllä jatkaa, mutta jos kiinniottaja on jossain muualla, sen ei tarvitsekaan tietää containerista mitään, sillä siihen mennessä pino on jo siivottu.
Omatekemien luokkien pitää olla aina exception safe. Tämä on C++-koodarin perusohjesääntö, jota noudattamalla elämä muuttuu paljon huolettomammaksi, varsinkin jos vielä käyttää tapaa, että varailee muistit ja muut resurssit konstruktoreissa ja vapauttaa ne viimeistään destruktoreissa (ns. RAII-idiomi).
Tämä ei toteudu tuossa Vector
-esimerkissä, jossa vector_delete(v)
voi vahingossa unohtua tekemättä.
C++:n kääntäminen on hitaampaa, koska kääntäjä käsittelee suuremman määrän tietoa kuin C-kääntäjä. C++ on myös C:tä laajempi kieli. Lopputuloksena syntyvä ajettava ohjelma ei kuitenkaan ole luonnostaan yhtään hitaampi kuin C:n tapauksessa, mahdolliset erot syntyvät muista syistä.
offtopic: ompa sparcini gcc ja g++ "sekaisin", g++ käänettynä on 20% nopeampi kuin gcc:llä. c++ versio (eli vähän hienompaa koodia, vectoria käyttää jne) taas on noiden kahden välissä.
Vertailuksi, x86:lla gcc:lla ja g++ käänettyt versiot ovat yhtä nopeita ja tiedostokokokin on g++ vaan vähän isompi (tunkee mukaan alle kilotavun sitä c++ runtimia, -fno-rtti -fno-exceptions tekee täysin samankokoiseksi, kun ei niitä C ohjelmassa kummiksaan tarvi).
Ohjelmana oli sellainen erikoinen breadth-first search (dt06 toinen tehtävä), alkoritmi c ja c++ versioissa sama, toteutus tokin eri. (Minun) c++ toteutus on sitten kai vaan huonompi.
c++ kääntäjän hitaus lienee vaan STL, sitä kun käänetään aina vähän erilaisena, templateja siis.
Ei kai voi mitään, on se c++ vähän hianompi kiäli.
Aihe on jo aika vanha, joten et voi enää vastata siihen.