Just nu i M3-nätverket
Gå till innehåll

Anropa metoder från härledda klasser indirekt


Onp

Rekommendera Poster

Har följande klasser:

 

[log]class Vehicle { /* ... */ };

 

class Car : public Vehicle

{

public:

void SetNumWheels(int);

 

private:

int mNumWheels;

};

 

class Boat: public Vehicle

{

public:

void SetNumEngines(int);

 

private:

int mNumEngines;

};[/log]

 

SetNumWheels() och SetNumEngines() är bara exempel för att illustrera metoder som är unika för respektive klass.

 

Okej. Om jag nu gör följande:

 

Vehicle* A = new Car;
Vehicle* B = new Boat;

 

Hur anropar jag SetNumWheels() från A? eller SetNumEngines() från B?

Jag är medveten om att det går typomvandla, eller att jag skaffar en Car-pekare eller en Boat-pekare. Men jag måste på nåt sätt komma åt dessa metoder från en Vehicle-pekare.

 

Hur löser man detta på en snyggt sätt?

 

[inlägget ändrat 2004-07-07 21:44:14 av Onp]

Länk till kommentar
Dela på andra webbplatser

Anjuna Moon

Klassen Vehicle ska ju vara superklass till Boat och Car. Iden med klasser är just klassystemet. Varför vill du att två syskongrenar ska kunna anropa varandra? Makes no sense objektorienterat men om du måste, så är det ju bara att använda Fader.AndraSyskonet. Missuppfattar jag vad du vill göra?

 

[inlägget ändrat 2004-07-07 21:57:35 av Anjuna Moon]

Länk till kommentar
Dela på andra webbplatser

Hur anropar jag SetNumWheels() från A? eller SetNumEngines() från B?

Vad menar du att du vill göra egentligen?

 

Är du i en metod i B och vill ropa på din instans B:s metod "SetNumEngines()" så säger du ((Boat*)this)->SetNumEngines().

Fast det bygger ju på att du vet att du kan göra en sådan typecast, t.ex genom rtti eller någon metod som returnerar ett typ-id.

 

Edit: Rättade en felaktig typecast.

Vidare, om metoderna verkligen är gemensamma (dvs bägge ska finnas i bägge klasserna) så ska de naturligtvis ligga på Vehicle och ärvas virtuellt precis som övriga skribenter säger.

[inlägget ändrat 2004-07-07 22:51:42 av fhe]

Länk till kommentar
Dela på andra webbplatser

Om du vill kunna anropa de härledda klassernas metoder genom en basklasspekare måste dessa metoder finnas i basklassen och vara virtuella. Emellertid kan man då fråga sig om det är vättigt att låta Boat och Car ärva från samma basklass... Här är ett expempel.

 

class Vehicle {

public:

virtual void start() { cout << "Vehicle::start()" << endl; }

virtual void stop() { cout << "Vehicle::stop()" << endl; }

};

 

class Car : public Vehicle {

public:

void start() { cout << "Car::start()" << endl; }

void stop() { cout <<"Car::stop()" << endl; }

};

 

class Boat: public Vehicle {

public:

void start() { cout << "Boat::start()" << endl; }

void stop() { cout << "Boat::stop()" << endl; }

};

 

 

int main() {

Vehicle *v1 = new Car;

Vehicle *v2 = new Boat;

 

v1->start();

v2->start();

 

v1->stop();

v2->stop();

 

delete v1;

delete v2;

return 0;

}

 

P.S. Hur f-n gör man såna där fancy grejer så man kan gömma koden? D.S.

 

 

Länk till kommentar
Dela på andra webbplatser

Anjuna Moon

manner: [LOG ] [/LOG ]

 

I övrigt, visst ska båda härledas från klassen Vehicle, då de båda faller under denna klass i det här fallet (transportmedel, språknazister undanbedes) men jag bibehåller åsikten att syskon inte ska anropa varandra då detta bryter mot iden med OO. Vill man av någon anledning göra detta så skapar man en ny "interference"-klass.

 

Länk till kommentar
Dela på andra webbplatser

Alltså, ni har nog missuppfattat lite. Syskonen ska inte anropa varandra.

 

Håller på med ett äventyrsspel där jag har en klass, Item, som är basklass åt Weapon, Car, etc. Dessa härledda klasser har metoder som Shoot() och Drive(). Alltså olika metoder för hur spelaren kan interagera med dem.

 

I min huvudklass, World (som är basklass åt alla klasser), har jag en std::vector med Item-pekare. Elementen i vektorn kan alltså innehålla härledda klasser.

 

Problemet är alltså hur jag ska kunna anropa Weapon's och Car's metoder utifrån denna vektor med Item-pekare. Man vill ju inte gärna ha en massa virtuella metoder i klassen World, eftersom det ska vara enkelt att lägga till nya objekt.

 

Länk till kommentar
Dela på andra webbplatser

Anjuna Moon

Det blev tydligare vad du vill göra nu, men du verkar tänka fel när det gäller vad som skall instantieras.

 

Får en spelare tag på en bil så ska spelaren tilldelas instansen av Car och inte av Item, det är där jag tycker att du gör fel. Du måste se instanserna istället för klasserna. Item skall bara innehålla grundattribut. och grundmetoder En instans av Item skall inte ens kunna existera i ditt fall.

 

Som jag gissar att du tänkt är att man skaffar sig en instans Item, som råkar var en bil, och sen vill du kunna köra den med Item.Drive(), men det är fel sätt att göra det på.

 

AnjunaMoon

____________________________________________________________

/* There is nothing more permanent than a temporary solution... */

 

Länk till kommentar
Dela på andra webbplatser

Hur ska man annars hålla reda på vilka objekt som finns i världen? Det går ju inte ha olika typer i en vektor, eller?

 

Länk till kommentar
Dela på andra webbplatser

Anjuna Moon
Hur ska man annars hålla reda på vilka objekt som finns i världen

Mm, satt och tänkte på det precis efter jag postade. Kanske det får bli virtuella metoder ändå. Har för mig att gamla Smalltalk fungerade på det sätt man vill ha (inte säker, det var ett tag sen), dvs att sökning av metoder propagerade neråt om de inte hittades i aktuell nivå.

 

Jag måste säga att jag är lite för

trött för att svara bättre, får sova på saken =)

AnjunaMoon

____________________________________________________________

/* There is nothing more permanent than a temporary solution... */

 

Länk till kommentar
Dela på andra webbplatser

Lär dig C++ på 3 veckor. Dag 12 - Arv.

Sida 391: "Man kan inte komma härifrån till dit"

 

Om Dog-objektet har en metod WagTail() som inte finns i Mammal, kan man inte använda pekaren till Mammal för att komma åt metoden (om man inte typomvandlar den till en Dog-pekare). WagTail() är inte en virtuell funktion och är inte definierad för Mammal-objektet, så det krävs ett Dog-objekt eller en Dog-pekare.

 

Du kan omvandla Mammal-pekaren till en Dog-pekare. Men det finns mycket bättre och säkrare sätt att anropa metoden WagTail(). C++ ogillar typomvandlingar eftersom det ofta orsakar fel. Mer om detta när vi talar om multipelt arv i Dag 16 och när vi tar upp mallar i Dag 20, "Undantag och felhantering".

 

Men jag hittar en det han refererar till :/

Det är ju exakt detta jag är ute efter.

 

Länk till kommentar
Dela på andra webbplatser

metoder ändå. Har för mig att gamla Smalltalk fungerade på det sätt man vill ha (inte säker, det var ett tag sen)

Kuriosa men Njae, Smalltalk (lixom ObjC och ett par andra språk) har messaging vilket i princip går ut på att man ger ett metodnamn till en instans och sen är det objektets sak att hitta det i kedjan (uppåt på samma sätt som i C++). Om metoden inte hittas så hamnar man i en metod (om den är definerad) som tar allt som ingen hanterade vilket är rätt praktiskt när man gör proxys och annat (i princip behöver man bara implementera en metod).

 

I princip är det bara den andra extremen på skalan från hård tidig bindning till ingen länkning/bindning alls.

 

 

Länk till kommentar
Dela på andra webbplatser

C++ ogillar typomvandlingar eftersom det ofta orsakar fel.

Det där är ett direkt fel. Så är det inte alls, tvärt om. Släng boken och gör din typecast.

 

Att multipelt arv av implentation (och inte bara interface som i t.ex java) är rätt trasigt i sin design gör ju inte att "c++ ogillar typomvandlingar", det är mer en bra anledning att inte syssla med MI av implementation om det inte är absolut nödvändigt (vilket det nästan aldrig är) och man kan det och vet exakt vad man håller på med. Men det finns religionkrig om det också så låt oss inte dra in multipelt arv i diskussionen.

 

Typecasta från (i ditt fall) Vehicle* till Boat* för att kunna göra anropet gör du med en enkel ((Boat*)B)->blaha() eller med någon av de typecast-opertorer som numera ingår i C++ (kör du inte med en gammal kompilator borde du ha dem). Du hittar dem här:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccelng/htm/express_74.asp

 

 

Länk till kommentar
Dela på andra webbplatser

Men om jag ska göra en typomvandling så måste jag ju veta vilken typ objektet är, och det är ju inte alltid jag vet det.

 

T.ex.

 

World* world;
Item* item;

// returnerar en Itempekare som pekar på ett Weapon-objekt
item = world->GetItem("knife");

 

Länk till kommentar
Dela på andra webbplatser

och det är ju inte alltid jag vet det.

Då får du ta reda på det.

Antingen gör du en pure virtual metod i basklassen som du måste implementera i alla subklasser och som du därför alltid kan ropa på för att fråga vad en instans är för en typ:

ex:

class Basklass {
public:
  virtual int type()=0;
}

class subklass : public Basklass {
public:
  virtual int type() {
      return 1;
  }
}

 

Eller också använder du C++ inbyggda Runtime Type Information och typeid-operatorn, exempel på det kan du hitta t.ex här:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_pluslang_typeid_operator.asp

 

 

Länk till kommentar
Dela på andra webbplatser

Arkiverat

Det här ämnet är nu arkiverat och är stängt för ytterligare svar.

×
×
  • Skapa nytt...