Gå til innhold

Returnere "inherited" objekt (klasse).


Gjest Slettet-Pqy3rC

Anbefalte innlegg

Gjest Slettet-Pqy3rC

Tenk følgende klasse A;

class A : public B, public C {
};

Er det her mulig i klassen A å lage en metode som returnerer adressen til klassen C ?

Det vil si noe sånt;

class A : public B, public C {
public:
   C * GetC() {
      return ?;
   };    
};

... men hva må da i tilfelle stå istedenfor spørsmålstegnet ?

 

 

---------------------------------------------------------------

Note;

Jeg veit at dersom C inneholder en ikke-privat metode som returnerer this;

class C  {
public:
   C * GetC() {
      return this;
   };    
};

burde jeg kunne kalle denne fra A;

class A : public B, public C {
public:
   C * GetC() {
      return C::GetC();
   };    
};

... men spørsmålet er altså om det er mulig å gjøre det på en annen måte.

Lenke til kommentar
Videoannonse
Annonse

Tenk følgende klasse A;

class A : public B, public C {
};

Er det her mulig i klassen A å lage en metode som returnerer adressen til klassen C ?

Det vil si noe sånt;

class A : public B, public C {
public:
   C * GetC() {
      return ?;
   };    
};

... men hva må da i tilfelle stå istedenfor spørsmålstegnet ?

 

 

---------------------------------------------------------------

Note;

Jeg veit at dersom C inneholder en ikke-privat metode som returnerer this;

class C  {
public:
   C * GetC() {
      return this;
   };    
};

burde jeg kunne kalle denne fra A;

class A : public B, public C {
public:
   C * GetC() {
      return C::GetC();
   };    
};

... men spørsmålet er altså om det er mulig å gjøre det på en annen måte.

 

class A : public B, public C {
  public:
    C* GetC() { return this;}
    B* GetB() { return this;}
    A* GetA() { return this;}

}

 

Men dette funker også

 A* testA = new A;
 B* testB = testA;
 C* testC = testA;

 std::cout << testA << std::endl;
 std::cout << testB << std::endl;
 std::cout << testC << std::endl;

hva er det egentlig du prøver å oppnå?

Lenke til kommentar
Gjest Slettet-Pqy3rC

Prøver å få litt forståelse av hva kompilatoren gjør og hvilke muligheter en har til å kontrolleres hva som returneres.

 

class clA {
};
class clB : public clA {
};
class clC : public clA {
};
class clD: public clB, public clC {
public:
   clD * Getd() {return this;}
   clC * Getc() {return this;}
   clB * Getb() {return this;}
   clA * Geta() {return this;}     // <- Do not compile
};

 

Getd() og Getb() returnerer samme adresse, Getc() en adresse som er avvikende. Det ser ut som kompilatoren klarer seg så langt.

 

Geta() linjen i eksempelet over er det værre med siden klassen clD inneholder to ulike clA klasser. Her blir det visst for mye for kompilatoren, finnes det noen måte å angi hvilken clA klassene/objektene som skal returneres ?

Endret av Slettet-Pqy3rC
Lenke til kommentar

Når du havner i slike situasjoner så er det her (etter min mening) hele systemet med multiple inheritance bryter sammen. Vanligvis blir det laget 2 instanser av A objektet (og det er sjelden det du vil?), men det kan man løse ved å arve virtual

ie,

class B : public virtual A {};
class C : public virtual A {};
class D : public virtual B, public virtual C {};

Da blir det bare laget èn instance av a som er delt av objektene.

 

I ditt eksempel må du spesifisere hvilken av A klassene du vil ha, og da må du spesifisere hvilken av parentene du tenker på. ie

clA * Geta() {return static_cast<clB*>(this);}  // vil returnere clB sin instance av A

bruk static_cast istede for dynamic_cast når du *vet* at objektet er av dypen du caster til. Casten resolves compile time og kompilatoren din vil bjeffe om du gjør noe feil. Dynamic cast er treg og bruker magisk run time type info for å caste!

 

Men gitt fra hvordan du har skrevet koden din, virker det som om du egentlig bør arve virtual her.

 

Mitt forslag om du havner i situasjoner som dette, er å se på arvestrukturen din om du heller kan gjøre noe av det du arver til medlemsvariabler istede. Ofte vil dette løse problemet og fjerne veldig kompliserte arvestrukturer. Min mening er at man bør unngå for lange og kompliserte arvehiearkier. (bruk heller medlemsvariabler!)

 

Eksempel på hva jeg mener: (arveeksempelet er ganske teit, men illustrerer hva jeg mener.. klarte ikke å tenke på en reell situasjon i farta)

class Posisjon {};
class Rotasjon {};
class Individ: virtual public Posisjon,  virtual public Rotasjon {};
class Person : public Individ, virtual public Posisjon, virtual public Rotasjon {};


// kan heller skrives som..
class Individ {
 public: 
 virtual const Posisjon& GetPos() const = 0;
 virtual void SetPos(const Posisjon& _pos) = 0;
 ...
};

class Person : public Individ  {
 private:
  Posisjon pos;
  Rotasjon rot;
 public:
 virtual const Posisjon& GetPos() const { return pos;}
 virtual void SetPos(const Posisjon& _pos) {pos = _pos;}

... 
}

 

Da slipper du unna virtual arving, og individ klassen din har her Konseptet om posisjon og Rotasjon (fordi den trenger det et eller annet sted), men definerer kun et interface, som baseklassene kan definere for den. (medlemsvariablene kunne like godt i *dette eksempelet* ligget i Individ klassen og Personklassen kunne vært "tom".

Endret av [kami]
Lenke til kommentar
Gjest Slettet-Pqy3rC

Takker for innspill.

 

Dette er som sagt kun "forskning" på ulike scenarioer i arbeidet med å dokumentere en metodikk. Jeg lager for tiden regler for metoder å løse ting på i et kommende prosjekt. Alt fra overordnede regler på navngivning av variabler, metoder filer osv. til hvordan koden skal bør/skrives under ulike scenarioer.

 

Jeg hadde nesten glemt xxx_cast helt, benyttes såpass sjelden. Det gir ihvertfall en mulighet akkurat her, takk.

 

Virtuell arving er jeg skeptisk til. Du får de samme variabel/medlem/property problemene der som når en jobber med tråder. F.eks to objekter som benytter samme "teller".

Jeg foretrekker å benytte "using xxx::xxx" for metodevalg dersom to varianter av samme subklasse oppstår (istedenfor å arve virtuelt). Desverre ken ikke benytte "using" på lignende måte når det gjelder referansen til selve klassene (derav denne tråden).

 

Det er et par ulemper med å benytte en klasse som medlemsvarabel istedenfor å arve den. Metodene blir ikke tilgjengelige for bruker av klassen og det må refereres til variabelen hele tiden i koden. Allikevel er jeg enig i at medlemsvarabel metoden å foretrekke i en del situasjoner.

 

Her er et reélt eksempel fra systemene jeg jobber med;

template <class T>
class ucBuffer : public ucRange<uc_USHRT>, protected ucCharacter, protected std::vector<T> {
.
.
.
  using ucRange::ClassId;
.
.
.
};

ucBuffer er grunnlaget for strengbehandlings klasser. Både ucRange og ucCharacter er arvet fra samme base klasser (minnehåndtering, klasseidentifikasjon, etc). Begge disse klassen benyttes så ofte i gjennom hele ucBuffer at å ha dem som variabler ville vært en plage mht alle referansene (gjør også koden "rotete").

Isteden benyttes using for å styre kall til metoder som er felles for både ucRange og ucCharacter.

Endret av Slettet-Pqy3rC
Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
  • Hvem er aktive   0 medlemmer

    • Ingen innloggede medlemmer aktive
×
×
  • Opprett ny...