Avatar billede hurra Novice
25. juni 2009 - 12:48 Der er 10 kommentarer og
1 løsning

const attributer og multipel nedarvning

Jeg mangler lidt forståelse for hvorfor min kode virker som den gør... Og så kan jeg ikke finde ud af at udvide min kode til det jeg egentligt skal bruge.

Jeg er ved at lave et program der kan håndtere en hel del forskellige slags måleinstrumenter. Derfor har jeg lavet et hiraki af generelle typer, og de er så vider specialiceret i konkrete typer af instrumenter.

Alle instrumenter skal indeholde deres navn, så det har jeg i den øverste klasse i mit hiraki.

class Instrument
{
  public:
    const char Name[];

    Instrument(const char Name[])
        : Name(Name) {};
}

Denne klasse bliver så bla. nedarvet i en strømforsynings klasse, hvis konstruktor kalder sin forfaders konstruktor

class InstrumentPSU : virtual public Instrument
{
  public:
    InstrumentPSU(const char Name[])
        : Instrument(Name) {};
}

Jeg har så en specifik strøm forsynings type, som jeg opretter i en klasse. Men hvorfor skal den både kalde konstruktoren i Instrument og InstrumentPSU ?

class AgilentE3631A : virtual public InstrumentPSU
{
  public:
    AgilentE3631A()
        : InstrumentPSU("HEWLET_PACKARD,E3631A"), Instrument("HEWLET_PACKARD,E3631A") {};
}

Jeg ville jo mene det var nok bare at kalde konstruktoren fra InstrumentPSU.

Hvis jeg lavet en default konstruktor i InstrumentPSU, og så nøjes med at kalde konstruktoren for Instrument i min specifikke klasse. Det hjælper da lidt på den fremtidige vedligeholdelse, men jeg undre mig bare...

Der ud over vil jeg gerne have at et instrument kan have flere navne (HP har skiftet navn til Agilent, og derfor kan samme instrument både hede HEWLET-PACKARD og AGILENT).

Det vil sige jeg vil gerne have 'Name' attributen til at være en 'const char *[]'. Hvordan for jeg initialiceret den fra min konstruktor? Noget i stil med:
    AgilentE3631A()
        : Instrument({"HEWLET_PACKARD", "AGILENT"})
Avatar billede hurra Novice
25. juni 2009 - 12:50 #1
I stedet for det sidste exempel ville det her måske have været bedre. Det er noget i den her stil jeg vil have, det sakl bare gøres fra konstriktoren:

const char *Names[] = { "HEWLETT-PACKARD", "AGILENT" };
Avatar billede hurra Novice
25. juni 2009 - 14:00 #2
Ok, jeg har fundet en løsning på den sidste del at mit spørgsmål. Er der en smartere måde at gøre det her på?

class AgilentE3631A: public InstrumentPSU
{
  public:
    static const char *AgilentE3631ANames[];

    AgilentE3631A()
        : InstrumentPSU(AgilentE3631ANames)
    {}
};
const char *AgilentE3631A::AgilentE3631ANames[] = {"HP", "AGILENT"};
Avatar billede segmose Nybegynder
25. juni 2009 - 14:46 #3
hvad fejl får du hvis du kun kalder den ene constructor?
skal den ene eller begge constructor være virtuel også?
Avatar billede hurra Novice
25. juni 2009 - 15:11 #4
Jeg har lidt forskellige muligheder for fejl jeg kan vælge mellem :)

    AgilentE3631A()
        : InstrumentPSU("HEWLET_PACKARD,E3631A") {};

giver en fejl at Istrument() ikke findes.

Hvis jeg laver en
    Instrument()
        : names(NULL)
    {
    };
Kan min kode kompilere, men navnet af min Agilent bliver NULL, hvilket jo da kunne indikere at Instrument konstruktoren bliver kaldt til sidste, selv om jeg ville mene det skulle være den første der blev kaldt.

Det første kode jeg har sendt, virker efter hensigten. Jeg kan bare ikke lige regne ud hvorfor jeg ikke kan kalde konstruktoren fra den klasse jeg arver fra, men i stedet bliver nødt til at kalde konstruktoren fra den øverst base klasse.

Grunden til at alle arver virtuelt er, at nogle instrumenter indeholder flere funktionaliteter, og derfor skal arve fra flere generelle instrumenter.
Avatar billede arne_v Ekspert
26. juni 2009 - 00:40 #5
Når du arver virtuelt så skal du kalde eksplicit på denne måde.
Avatar billede arne_v Ekspert
26. juni 2009 - 01:20 #6
Prøv og studer følgende eksempel:

#include <iostream>

using namespace std;

class B1
{
    private:
        int v;
    public:
        B1(int v) { this->v = v; }
        int GetV() { return v; }
};

class D1A : public B1
{
    public:
        D1A() : B1(123) { }
        void PrintA() { cout << GetV() << endl; }
};

class D1B : public B1
{
    public:
        D1B() : B1(456) { }
        void PrintB() { cout << GetV() << endl; }
};

class DD1 : public D1A, public D1B
{
    public:
    DD1() : D1A(), D1B() { }
};

class B2
{
    private:
        int v;
    public:
        B2(int v) { this->v = v; }
        int GetV() { return v; }
};

class D2A : virtual public B2
{
    public:
        D2A() : B2(123) { }
        void PrintA() { cout << GetV() << endl; }
};

class D2B : virtual public B2
{
    public:
        D2B() : B2(456) { }
        void PrintB() { cout << GetV() << endl; }
};

class DD2 : public D2A, public D2B
{
    public:
    DD2() : D2A(), D2B(), B2(789) { }
};

int main()
{
    D1A o1;
    o1.PrintA();
    DD1 oo1;
    oo1.PrintA();
    oo1.PrintB();
    D2A o2;
    o2.PrintA();
    DD2 oo2;
    oo2.PrintA();
    oo2.PrintB();
    return 0;
}

Hvad skulle de 2 sidste Print kald udskrive hvis ikke B2 constructor var kaldt i DD2 constructor ?
Avatar billede hurra Novice
26. juni 2009 - 10:07 #7
Det kom jeg godt i tanke om her i nat en gang, altså at problemet er, at jeg atver virtuelt :)

Hvis ikke B2 konstruktoren blev kaldt fra DD2, ville det jo give mest mening at de 2 sidste Print() skulle skrive hhv 123 og 456. Men da de arver virtuelt, er der kun én v, og dermed vil den have værdien af den sidste der har skrevet til den, hvilket ville være 456.

Nu hvor B2 konstruktoren bliver kaldt, er det den der er den sidste der skriver til den, og derfor skriver de begge 789.

Men nu har jeg fået en ny ting at undre mig over. Hvorfor virker dit exempel med B1 og DD1? Jeg troede jo at når man laver multipel arv, uden at gøre det virtuelt, ville man få flere forekomster af de variabler der er erklæret i de øvre baser. Det vil så i dette tilfælde sige, at jeg var sikker på at exemplet ville give en compiler fejl i DD1, fordi v blev erklæret flere gange. Jeg er faktisk helt sikker på at jeg har set klasse exemplet (den med person; studerende:person; lære:person; tutor:studerende,lære ) på dette fejl.
Avatar billede hurra Novice
26. juni 2009 - 10:09 #8
En anden ting jeg så heller ikke havde troet muligt i B1/DD1 exemplet, var castning til de forskellig base typer. Men det virker tilsyneladende også:)

    D1A *tp1 = &oo1;
    D1B *tp2 = &oo1;
    tp1->PrintA();
    tp2->PrintB();
Avatar billede arne_v Ekspert
26. juni 2009 - 15:08 #9
1 virker fint. Ja - der er to forekomster af B1 hvilket ses tydeligt af at de to Print udskriver forskellige tal. Det er ikke et problem at der er en v i begge forekomster.
Avatar billede arne_v Ekspert
26. juni 2009 - 15:10 #10
super type = sub type

virker altid.

En DD1 er en D1A og man kan derfor assigne. Ditto med DD1 og D1B.
Avatar billede arne_v Ekspert
04. august 2009 - 03:23 #11
OK ?
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester