520 likes | 635 Vues
C++ for Java-programmerere. Åsmund Eldhuset for Abakus Fagkom asmunde *at* stud .ntnu.no http://www.idi.ntnu.no/~asmunde/cpp/. Forutsetninger. Du er noenlunde komfortabel med Java Du kjenner følgende konsepter innen objektorientert programmering: Klasser Innkapsling Arv Polymorfi
E N D
C++ for Java-programmerere Åsmund Eldhuset for Abakus Fagkom asmunde *at* stud.ntnu.no http://www.idi.ntnu.no/~asmunde/cpp/
Forutsetninger • Du er noenlunde komfortabel med Java • Du kjenner følgende konsepter innen objektorientert programmering: • Klasser • Innkapsling • Arv • Polymorfi • Grensesnitt (interfaces)
Litt historie • C ble utviklet på 70-tallet av Brian W. Kernighan og Dennis M. Ritchie • Designet for systemprogrammering under UNIX • C++ ble utviklet på 80-tallet av Bjarne Stroustrup • Bygger på C; første utgave het "C with classes" • Hentet mange ideer fra norskutviklede Simula (verdens første objektorienterte språk) • Både C og C++ er åpne og ISO-standardiserte
Argumenter for C++ • Raskere enn Java • Java kjøres av en Virtual Machine • C++ kompileres til maskinkode • Gir nærkontakt med systemet • Innfallsport til C, som er enda bedre egnet til system-programmering (operativsystemer, mikrokontrollere etc.) • Du kan gjøre ALT som er mulig å gjøre med datamaskinen • Ett av de mest utbredte programmeringsspråkene; mange eksisterende systemer er skrevet i C++ • Mange ferdigskrevne biblioteker • Nerdefaktor ;-)
Argumenter mot C++ • Mindre nybegynnervennlig • Lett å skyte seg selv så til de grader i foten • Uforsiktighet kan føre til obskure minnelekkasjer • Portabiliteten varierer med hva man foretar seg • Utvikling tar generelt sett lengre tid enn med Java og C#
Når bruke C++? • Når du trenger rå hastighet (spill, grafikk, tungregning, algoritmekonkurranser) • Når du driver systemprogrammering • Når du arbeider med eksisterende C++-systemer • Når du har lyst til å leke deg med programmering
Java Proprietært (Sun) Garbage collection Kjøres av en VM Samme kompilerte kode kan kjøres overalt Bruker nesten samme syntaks som C++ C++ Åpent Manuell minnebehandling Kompileres til maskinkode Må rekompileres for hvert nye system; koden kan måtte endres C++ vs. Java
Kompilatorer og IDE'er • Kompilator oversetter koden til maskinkode • IDE = Integrated Development Environment (kombinerer editor, kompilator og debugger) • Linux: • Kompilator: gcc / g++ • Debugger: gdb • Editor: det du foretrekker • Windows: • Microsoft Visual Studio (http://msdn.microsoft.com/vstudio/express/visualc/) • Bloodshed Dev-C++ (http://www.bloodshed.net/devcpp.html)
Bruk av g++ • Kompilering: g++ kildefil • Kjøring: ./a.out • Kompilere flere filer: g++ kilde1 kilde2 ... • Endre navnet til outputfilen:g++ -o outputfil kildefil./outputfil • Skyte seg selv i foten:g++ -o kildefil kildefil(sletter kildekoden din)
Minimalt program • int main() {return 0;} • Vi legger merke til: • Typen til main() er int, ikke void • Ingen String [] args (skal programmet ta imot argumenter, skriv int argc, char * argv []) • main() er ikke public staticog står ikke inni noen klasse
Hello World // Dette er et Hello World-program #include <iostream> using namespace std; int main() { cout << "Hello World!" << endl; return 0; }
Hello World • Kommentarer lages med // eller /* */ • cout << x << endl; tilsvarer System.out.println(x); • #include minner om import, men fungerer ved å "lime inn" en fil (mer om dette senere) • iostream er et bibliotek som inneholder IO-støtte (f.eks. cout) • using namespace minner også om import. Uten dette måtte vi skrive std::cout og std::endl, fordi cout og endl tilhører et namespace (tilsv. en pakke) som heter std. • Man kan ha funksjoner utenfor klasser; main() skal alltid være en slik funksjon
Input og output • cout << x; skriver data til skjermen • cout << endl; skriver et linjeskift • cin >> x; leser data fra tastaturet • cin merker hvilken datatype variablene har • cin og cout kan "cascade"s:cout << x << y << z;(skriver ut dataene uten mellomrom mellom)cin >> x >> y >> z;(leser først inn i x, så i y, så i z)
Variabler • Variabler lages og brukes på samme måte som i Java • C++ har følgende primitive datatyper: • bool (tilsvarer boolean) • char (tilsvarer byte og til en viss grad char) • short • int • long • float • double • unsigned kan brukes foran heltallstypene • Funksjoner kan også ha void som type
If/else, for, while • ... fungerer på samme måte som i Java! • Kun én forskjell: betingelser i if/else og while kan være heltall (0 blir false, alt annet blir true) • int x, y;cin >> x >> y;if (y) cout << x / y << endl;else cout << "Nevneren er 0!" << endl;
Deklarasjon av funksjoner • Før en funksjon brukes, må kompilatoren vite at funksjonen eksisterer • Dette kan gjøres på to måter: • Funksjonen kan allerede være definert (dvs. at funksjonskoden står lenger oppe i kodefilen) • Definisjonen kan finnes i en annen fil, og funksjonen kan deklareres (dvs. at man bare skriver metodenavnet) • Eksempel: funksjonenint square(int n) { return n * n; }kan deklareres slik:int square(int);
Biblioteker og headerfiler • C++-kompilatoren kompilerer én fil av gangen uavhengig av alle de andre • Biblioteksfunksjonene ligger i ferdigkompilerte filer • Headerfiler inneholder deklarasjoner som forteller hvordan biblioteksfunksjonene ser ut • Hvis f.eks. kompilatoren ser int square(int);, stoler den på at det finnes en slik funksjon. Den kompilerer koden som bruker square() og prøver etterpå å koble inn square() sin egen kode • Sammenkoblingsprogrammet kalles for en linker, og er som regel en del av kompilatoren
Biblioteker og headerfiler • Man kan selv lage prosjekter som består av flere kodefiler • Koden deles opp i kodefiler (.cpp) og headerfiler (.h) • Headerfilene inneholder deklarasjoner (navn og parameterlister for metoder og klasser) • Kodefilene inneholder definisjoner (den faktiske implementasjonen / koden) • #include "limer inn" koden fra en fil, og skal bare brukes for å inkludere headere
p Pekere • En variabel som inneholder en minneadresse • Bruk * i deklarasjoner for å lage "peker-til-datatype" • Bruk & for å finne adressen til en variabel • Bruk * for å dereferere en peker (lese innholdet i adressen det pekes til) • C++ bruker NULL i stedet for null • int a = 42;int * p = NULL;p = &a;cout << p << endl;cout << *p << endl; p a 0x32 42 0x04 0x32 a 42
p q a 3 42 Triksing med pekere int a = 3; int * p, * q; p = &a; q = &a; *p = 42; cout << a << endl; cout << *q << endl;
q p r Peker-til-peker-til... int a, * p, ** q, *** r; a = 42; p = &a; q = &p; r = &q; cout << r << endl; cout << *r << endl; cout << **r << endl; cout << ***r << endl; a 42
Funksjonsargumenter • Alle argumenter sendes som kopi • Følgende gir dermed ikke det tenkte resultatet:void swap(int a, int b) {int temp = a; a = b; b = temp;} • Vi sender pekere i stedet, for da får vi modifisert dataene gjennom pekerne:void swap(int * a, int * b) {int temp = *a; *a = *b; *b = temp;} • Sistnevnte funksjon må kalles slik: swap(&a, &b)
Referanser • Et tredje alternativ er å bruke referanser • En referanse er en mellomting mellom peker og "rådata" • Er ikke en kopi av dataene, så endring av en referanse fører til endring av de originale dataene • Kan ikke være NULL • Aksesseres som om det var rådata • void swap(int & a, int & b) {int temp = a; a = b; b = temp;} • Kalles slik: swap(a, b)
Arrays • Et array er bare et sammenhengende minneområde av samme datatype • En peker kan derfor brukes til å peke til starten av et array • Arrays lages slik:int * array = newint [4]; array
Arrays • Arrays er 0-indekserte og aksesseres som i Java:array[2] = array[1] + array[0]; • Bak kulissene foregår oppslag på array[i] slik: • Adressen i array leses • Størrelsen til datatypen som array peker til ganges med i og adderes til adressen for å finne hvor dataene ligger array 0x32 4 bytes 0x32 0x36 0x40 0x44
Arrays • Arrays vet ikke hvor store de er, og du får ingen exception hvis du går utenfor grensene • int * array = newint [8];for (int i = -1; i <= 8; ++ i) cout << array[i] << endl; • Ingen feilmelding • Du får ut data • Programmet kan krasje noen ganger, og fortsette andre ganger • Etter bruk må et array slettes:delete [] array;
Flerdimensjonale arrays • int ** array = newint * [3];for (int i = 0; i < 3; ++ i) array[i] = newint [4]; array
Klassedeklarasjoner • Klasser i C++ kan ikke være public/private • Deklarasjon og definisjon bør skilles • Deklarasjonen avsluttes med et semikolon • Constructors fungerer som i Java • Deklarasjon:class Person { Person(std::string name, int age);void presentYourself(); std::string name;int age;};
Klassedefinisjoner • this er en peker, og man bruker -> for å lese medlemmer via pekere • Person::Person(string name, int age) {this->name = name;this->age = age;}void Person::presentYourself() { cout << "Hei, jeg heter " << name << " og er " << age << " år gammel." << endl;}
Innkapsling • Tre nivåer: public, protected, private • public og private er det samme som i Java • Siden C++ ikke har pakker, finnes ikke package/default visibility, og protected gir synlighet bare for subklassene • I stedet for å sette modifikatoren foran hver variabel/funksjon, setter man den som "overskrift"
Innkapsling class Person {public: Person(std::string name, int age);void presentYourself();private: std::string name;int age;};
Objekter • Man har både objektvariabler og objektpekere • Objektvariabler inneholder selve objektet, og oppfører seg som om det var en variabel av primitiv datatype – forsvinner automatisk når funksjonen avsluttes • Objektpekere oppførerer seg som Java sine objektvariabler – må allokeres og deallokeres • . brukes på objektvariabler, og -> på objektpekere • Objektvariabler:Person p("Aasmund", 20);p.presentYourself(); • Objektpekere:Person * p = new Person("Aasmund", 20);p->presentYourself();delete p;
Arv • Tre typer arv: public, protected og private • Vi skal bare se på public, som tilsvarer Java sin type arv • C++ har ikke noe Object som alle klasser arver implisitt (men du kan lage et selv hvis du vil) • C++ støtter multiple inheritance, dvs. at en klasse kan arve fra flere klasser • Java: class Professor extends PersonC++: class Professor : public Person
Virtuelle funksjoner • Hvis du har følgende i Java:Person p = new Professor();p.presentYourself();er det Subclass sin versjon av someFunction() som kjøres • For å få til dette i C++ må man sette virtual foran funksjonsnavnet • Ikke-virtuelle funksjonskall bestemmes ved kompileringstid ut i fra pekertypen • Virtuelle funksjonskall bestemmes ved kjøretid ut fra den egentlige typen til objektet
Abstrakte klasser og funksjoner • Abstrakte funksjoner kalles for "pure virtual" • C++ har ikke abstract • Sett = 0 etter en funksjon for å gjøre den abstrakt:int someAbstractFunction(int x) = 0; • Klasser som inneholder "pure virtual"-funksjoner blir automatisk abstrakte (selv hvis de inneholder vanlige funksjoner også)
Abstrakte klasser og funksjoner • Java:public abstract class BankAccount {private int balance;public abstract void deposit(int);} • C++:class BankAccount {public:void deposit(int) = 0;private:int balance;};
Abstrakte klasser og funksjoner • Abstrakte klasser kan ikke instansieres • new BankAccount() er ikke tillatt • Subklasser som ikke implementerer alle "pure virtual"-funksjoner blir også abstrakte • class SavingsAccount() : public BankAccount {public:void deposit(int amount) { balance += amount - 100; }};
Interfaces • C++ har ikke interfaces, men variabelløse, abstrakte klasser gjør samme nytten • Java:public interface IRobberyVictim { void getRobbed(int amount);} • C++:class IRobberyVictim {public:virtual void getRobbed(int amount) = 0;};
Interfaces • Java:publicclass Person implements RobberyVictim • C++:class Person : public RobberyVictim
Arrays med objekter • Primitive typer:int * intArray = newint [4]; • Objekter:Car * carArray = new Car [4]; • Objektpekere:Car ** carPointerArray = new Car * [4];for (int i = 0; i < 4; ++ i) carPointerArray[i] = new Car();
Arrays med objekter a a[0] a[1] a[2] a[3] car car[0] car[1] car[2] car[3]
Arrays med objekter cp cp[0] cp[1] cp[2] cp[3] *cp[3] *cp[2] *cp[1] *cp[0]
Minnebehandling • C++ har ingen garbage collector • Du er selv ansvarlig for å frigi minne du ikke trenger lenger • Verktøy for dette: delete, delete [], destructors • Regler for trygg minnebehandling: • Ta alltid vare på resultatet fra new • Hver gang du skriver new, husk å plassere en delete på et passende sted • Et objekt som new'er noe bør også delete det (bruk destructors)
Destructors • Klasser kan definere en destructor (bare én, uten parametre), som vil kalles når objektet destrueres • Syntaks:class MyClass {public: ~MyClass();};MyClass::~MyClass() { ... }
delete og delete [] • delete frigir minne for et enkelt objekt/primitiv type:int * a = new int;delete a;Car * c = new Car();delete c; • Hvis delete kalles på en objektpeker, kaller den destructoren til objektet før minnet frigis • delete [] frigir minne for et array • Bruk aldri delete på noe du lagde med new [], og bruk ALDRI delete [] på noe du lagde med new • For de som kan C: Bruk ALDRI free() på noe du lagde med new eller delete på noe du lagde med malloc()
delete og delete [] • delete [] på et objektarray kaller destructors: • Car * carArray = new Car [4];delete [] carArray; • delete [] på et objektpekerarray kaller ikke destructors:Car ** carPointerArray = new Car * [4];for (int i = 0; i < 4; ++ i) carPointerArray[i] = new Car();for (int i = 0; i < 4; ++ i)delete carPointerArray[i];delete [] carPointerArray;
Templates • Lar deg skrive generisk kode som fungerer med flere datatyper • template<typename T>void swap(T * a, T * b) { T temp; temp = *a; *a = *b; *b = temp;} • Hver gang du bruker en template-funksjon på en bestemt datatype, lages en ny utgave av koden for funksjonen