Programmera spel i C++ för nybörjare/Sprites och spelpjäser 6
(Genomgången förutsätter att du har en fungerande installation av Microsoft Visual C++ 2010 Express och SFML 1.6 på din dator.)
Konstanta medlemsfunktioner och statiska medlemsvariabler
redigeraAnta att vi har klassen stridsvagn:
class stridsvagn { public: //Konstruktordeklaration, definition utanför klassdeklarationen stridsvagn(int hastighet, char typ, double spelare_x, double spelare_y);//startvärden //Destruktion ~stridsvagn(){}; int hastighet; //Hur snabb är den double spelare_x; // var är den i sidled i programmet double spelare_y; //var är den i höjdled i programmet char typ; sf::Sprite sprite; }; //Konstruktionsdeklaration-------------------------------------------------- stridsvagn::stridsvagn (int ut_hastighet, char ut_typ, double ut_spelare_x, double ut_spelare_y) { hastighet=ut_hastighet; typ=ut_typ; spelare_x=ut_spelare_x; spelare_y=ut_spelare_y; std::cout << "En stridsvagn är byggd!" << endl; }
När man skapat sin klass och börjar att använda den finns det vissa saker man kan tänka på, bara för att vara extra säker.
Public och private (och protected)
redigeraSom nämnts tidigare är uppdelningen mellan public och private i en klass ett skydd för dig. Du skall inte kunna ändra saker av misstag. ”Det gör jag aldrig” kanske du säger, men faktum är att om du lagt ett projekt på hyllan en längre tid och sedan börjar med det igen är det lätt att glömma vad det var man hade för variabler i klassen som inte fick ändras i onödan. Grundregeln i klasser är att variablerna skall vara privata, medan funktionerna som kan ändra variablerna skall vara under public.
Protected
redigeraProtected är det som man som nybörjare i C++ världen har svårast att greppa. Protected är exakt samma sak som private i en klass som inte har någon form av arv vidare till nya klasser.
Ett problem, eller en fördel om man så vill, med klasser i C++ är att det som finns under private normalt sett inte ärvs vidare, bara det som står under public. Ibland vill man få med sig det som står under private också och för att komma runt det dilemmat har man infört varianten protected. En variabel eller funktion under protected är lika säker som en variabel som står som private i sin egen klass, men den variabeln eller funktionen kan ärvas vidare.
Här är ett litet exempel som inte kräver SFML. Vi för in en variabel som är public, protected och private. Sedan anropar vi samtliga vid starten av programmet. Som du ser (om du lägger in koden) fungerar det hur bra som helst.
#include "stdafx.h" #include <iostream> using namespace std; class testare { public: testare(int innummer); //Konstruktor int allmaentnummer; //En publik variabel void visa_siffrar(int siffra_att_visa) //En funktion { std::cout << "Siffran som skall visas =" << siffra_att_visa << endl; } protected: int skyddatnummer; //En protected/skyddad variabel private: int privatnummer; //En privat variabel }; testare::testare(int utnummer) { //För att se skillnad på dem allmaentnummer = utnummer; //publikt nummer skyddatnummer = utnummer * 2; //protected blir dubbelt så stort privatnummer = utnummer * 4; //private blir 4 ggr större std::cout << "Allmaentnummer= " << allmaentnummer << endl; std::cout << "Skyddatnummer= " << skyddatnummer << endl; std::cout << "Privatnummer= " << privatnummer << endl; visa_siffrar(allmaentnummer); // Kommer åt publikt nummer visa_siffrar(skyddatnummer); // Kommer åt protected/skyddat nummer visa_siffrar(privatnummer); // Kommer åt private nummer } int main () { testare(1); //Kontrollera om man kommer åt värdena return 0; };
Om vi sedan låter en annan klass ärva vår nyskapade klass kommer du att se att värdena ärvs, men man kan inte längre komma åt vare sig protected eller private längre. Du får ta bort // markeringarna för att se att det blir fel i koden just där:
#include "stdafx.h" #include <iostream> using namespace std; class testare { public: testare(int innummer); //Konstruktor int allmaentnummer; //En publik variabel void visa_siffra(int siffra_att_visa) //En funktion { std::cout << "Siffran som skall visas =" << siffra_att_visa << endl; } protected: int skyddatnummer; //En protected/skyddad variabel private: int privatnummer; //En privat variabel }; testare::testare(int utnummer) { //För att se skillnad på dem allmaentnummer = utnummer; //publikt nummer skyddatnummer = utnummer * 2; //protected blir dubbelt så stort privatnummer = utnummer * 4; //private blir 4 ggr större std::cout << "Allmaentnummer= " << allmaentnummer << endl; std::cout << "Skyddatnummer= " << skyddatnummer << endl; std::cout << "Privatnummer= " << privatnummer << endl; visa_siffra(allmaentnummer); // Kommer åt publikt nummer visa_siffra(skyddatnummer); // Kommer åt protected/skyddat nummer visa_siffra(privatnummer); // Kommer åt private nummer } //En ärvd klass class aervd_testare : public testare { public: aervd_testare(int ut_siffra): testare(ut_siffra){}; }; int main () { testare testare1(1); //skapa testare 1, Skriver ut alla 3 så vi ser att de finns testare1.visa_siffra(testare1.allmaentnummer); // Kommer åt publikt nummer //testare1.visa_siffra(testare1.skyddatnummer); // Kommer inte åt protected/skyddat nummer //testare1.visa_siffra(testare1.privatnummer); // Kommer inte åt private nummer //En ärvd testare----------------------------------------------------------------------------------- aervd_testare testare2(2); //Skapa testare 2, Skriver ut alla 3 så vi ser att de finns testare2.visa_siffra(testare2.allmaentnummer); // Kommer åt publikt nummer //testare2.visa_siffra(testare2.skyddatnummer); // Kommer inte åt protected/skyddat nummer //testare2.visa_siffra(testare2.privatnummer); // Kommer inte åt private nummer return 0; };
Konstant medlemsfunktion
redigeraOm man verkligen vill köra med både hängslen och livrem kan man lägga till ordet "const" bakom en funktion som är kopplad till en klass. Gör man så ser man till så att även om man ändrar funktionen senare kommer den aldrig att kunna ändra en variabel inuti en klass. Anta att man har en funktion som skall kontrollera hur mycket bränsle det finns i en stridsvagn.
int kolla_bensin();
Funktionen finns där för att man skall få en siffra tillbaka som visar bränslemängden. Anta att jag senare ändrar den funktionen vid något förvirrat ögonblick så att om bränslet == 0 (tom tank) får tanken 5 liter extra. Helt plötsligt får sedan varken stridsvagnen eller någon annan class som ärvt stridsvagnen slut på bensin. För att förhindra att funktionen ändrar någon variabel i klassen, vare sig den är private eller public, skriver vi så här:
int kolla_bensin() const;
Funktionen har blivit en konstant funktion som inte kan förändra ett värde, bara läsa av det.
Statisk medlemsvariabel
redigeraDet finns andra finesser med "static" förutom att skydda variabler. Anta att du vill veta exakt hur många pansarvagnar som byggts i ett spel. Då kan du naturligtvis skapa en lista där du sparar någon form av identitetsnummer för varje stridsvagn, då kan du läsa av hur många poster som finns i listan för att se hur många som tillverkats. Eller så skapar du en ”statisk medlemsfunktion” tillsammans med en ”statisk medlemsvariabel” (eller ”static data member” som den heter på engelska) dvs. en medlemsfunktion som gäller samtliga instanser av en klass som läser en variabel som är gemensam för alla instanser av klassen. Du har klassen stridsvagn som har identiteten 0, varje stridsvagn som skapas, som kopia eller med kommandot ”new” från den, kan ha ett nummer som automatiskt räknas upp för varje ny, skapad stridsvagn.
Skapa en funktion som är ”inline”, dvs. helt och hållet befinner sig inuti klassen, under public: så att man når den utifrån. Under variablerna skriver man:
Static int antal_stridsvagnar; //Statisk medlemsvariabel eller static data member
Under funktionerna skriver man:
static int visa_antal_stridsvagnar(); { return antal_stridsvagnar; }
Lägg in så att antal_stridsvagnar räknas upp när man skapar en ny med
++antal_stridsvagnar
i konstruktionsdeklarationen.
Nollställ räknaren med en funktion alldeles före main.
Int stridsvagn::antal_stridsvagnar = 0;
Vill jag läsa av hur många stridsvagnar som skapats när jag är inuti main, skriver jag bara
cout << ”antal byggda= ” << stridsvagn:: antal_stridsvagnar() << endl;
Det som är finurligt är att jag dessutom kan komma åt det värdet i vilken som helst instans av klassen. Anta att jag skapat tre stridsvagnar: stridsvagn1, stridsvagn2, stridsvagn3.
Då kan jag lika gärna skriva:
cout << ”antal byggda= ” << stridsvagn1.antal_stridsvagnar << endl; cout << ”antal byggda= ” << stridsvagn2.antal_stridsvagnar << endl; cout << ”antal byggda= ” << stridsvagn3.antal_stridsvagnar << endl;
Om bara tre byggts ger samtliga cout värdet 3 även om jag avläser värdet i olika instanser.
Koden ser ut så här:
#include <iostream> #include <SFML/System.hpp> #include <SFML/Window.hpp> #include <SFML/Graphics.hpp> #define SFML_STATIC //Se till så att det inte behövs extra DLL-filer class stridsvagn { public: //Konstruktordeklaration, definition utanför klassdeklarationen stridsvagn(int hastighet, char typ, double spelare_x, double spelare_y);//startvärden //Destruktion ~stridsvagn(){}; int hastighet; //Hur snabb är den double spelare_x; // var är den i sidled i programmet double spelare_y; //var är den i höjdled i programmet char typ; sf::Sprite sprite; static int antal_stridsvagnar; //Statisk medlemsvariabel eller static data member static int visa_antal_stridsvagnar() //Används för att visa antalet i moderklassen { return antal_stridsvagnar; } }; //Konstruktionsdeklaration-------------------------------------------------- stridsvagn::stridsvagn (int ut_hastighet, char ut_typ, double ut_spelare_x, double ut_spelare_y) { hastighet=ut_hastighet; typ=ut_typ; spelare_x=ut_spelare_x; spelare_y=ut_spelare_y; ++antal_stridsvagnar; std::cout << "En stridsvagn är byggd!" << std::endl; } int stridsvagn::antal_stridsvagnar = 0; //Definition utanför klassdeklarationen int main (int argc, char *argv)//Startar programmet, allt syns enbart i konsollfönstret { //main startar stridsvagn stridsvagn1(50,'s',100,100); stridsvagn stridsvagn2(50,'s',100,100); stridsvagn stridsvagn3(50,'s',100,100); std::cout << "antal byggda(1)= " << stridsvagn1.antal_stridsvagnar << std::endl; //Värdet i en instans std::cout << "antal byggda(2)= " << stridsvagn2.antal_stridsvagnar << std::endl; std::cout << "antal byggda(3)= " << stridsvagn3.antal_stridsvagnar << std::endl; std::cout << stridsvagn::visa_antal_stridsvagnar() << std::endl; //Eller med inbyggd funktion i moderklassen return 0; }//Main slutar
Varför fungerar det här egentligen? Jo, genom att "antal_stridsvagnar" är definierad som en statisk variabel nollställs den inte mellan funktionskörningarna utan behåller sitt värde. Detta kan man också göra med vanliga variabler i ett program som inte innehåller klasser