Forum: Compiler & IDEs Abstrakte Basisklasse - Fehlermeldung in C++


von goldeneyes1987 (Gast)


Lesenswert?

Hi,

ich verstehe folgende Fehlermeldung nicht:
1
expected constructor, destructor, or type conversion before '=' token


Ich habe eine Abstrakte Basisklasse IDevice von dieser wird RadioControl 
abgeleitet.

Die letzte zeile ist für den Fehler verantwortlich.
1
IDevice* testDevice[10]; // Array's, with pointers of Objects from IDevice
2
RadioControl myDevice;
3
4
testDevice[0] = &myDevice;

Jedoch sehe ich hier keinen Fehler, oder doch ???


Vielen Dank schonmal an ALLE,

GoldenEyes

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Schau' dir doch mal an, wie das nach dem Präprozessor aussieht
(Compiler mit der Option -E starten).

von Karl H. (kbuchegg)


Lesenswert?

goldeneyes1987 schrieb:

> Die letzte zeile ist für den Fehler verantwortlich.
>
>
1
> IDevice* testDevice[10]; // Array's, with pointers of Objects from
2
> IDevice
3
> RadioControl myDevice;
4
> 
5
> testDevice[0] = &myDevice;
6
>


Wo genau hast du das stehen?

Zeig mehr Code

von goldeneyes1987 (Gast)


Lesenswert?

Hier :
1
// IDevic.h
2
3
class IDevice
4
{
5
  public:
6
    /** default destructor */
7
     virtual ~IDevice(){}
8
    
9
10
     virtual void fcn() = 0;
11
12
  protected:
13
     /** default constructor */
14
     IDevice(){}
15
16
17
  private:
18
    // members
19
    
20
};
21
22
// RadioControl.h
23
24
25
26
class RadioControl : public IDevice
27
{
28
  public:
29
   
30
31
    /** 
32
    * default constructor
33
    **/
34
  RadioControl(){}
35
36
    /**
37
     * default destructor
38
     * */
39
  virtual ~RadioControl(){}
40
41
42
  virtual void fcn(){};
43
44
45
  protected:
46
47
  private:
48
  // members
49
};

Mehr code ist eigentlich nicht dahinter!

von goldeneyes1987 (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Wo genau hast du das stehen?

Ja srry! // In Application.cpp


1
#include <IDevice.h>
2
#include <RadioControl.h>
3
4
IDevice* testDevice[10]; // Array's, with pointers of Objects from IDevice
5
RadioControl myDevice;
6
7
testDevice[0] = &(myDevice);

von Simon K. (simon) Benutzerseite


Lesenswert?

Eine Zuweisung außerhalb einer Funktion/Methode? Ist das erlaubt?

von Yalu X. (yalu) (Moderator)


Lesenswert?

goldeneyes1987 schrieb:
> testDevice[0] = &(myDevice);

Das ist eine Anweisung. Sie kann nur innerhalb einer Funktion oder
Methode stehen, was bei dir aber nicht der Fall ist.

Simon K. schrieb:
> Eine Zuweisung außerhalb einer Funktion/Methode? Ist das erlaubt?

Nein.

von Stefan (Gast)


Lesenswert?

Hi,
warum ist in deiner Basisklasse der Konstruktor "protected"? Die 
Subklasse kann doch damit gar nicht auf den Konstruktor der Basisklasse 
zugreifen, oder seh ich jetzt was nicht? :)

Gruß
Stefan

von goldeneyes1987 (Gast)


Lesenswert?

Natürlich nicht! Ach man, immer diese Burn Out's :-)

Vielen Dank!
Aber noch eine Frage in diesem Zusammenhang!

In wie weit macht die Kombination von virtual und inline einen Sinn?

von Stefan (Gast)


Lesenswert?

Sorry,
vergiss meine Anmerkung, bin übernachtet. Protected passt schon für die 
Subklsse :)

Gruß
Stefan

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

goldeneyes1987 schrieb:
> In wie weit macht die Kombination von virtual und inline einen Sinn?

Die hat keinen Sinn, denn die tatsächlich zutreffende Methode kann ja
bei late binding zwangsweise erst zur Laufzeit ermittelt werden.
Damit ist es dem Compiler unmöglich, sie bereits zur Compile-Zeit
inline einzufügen.

von (prx) A. K. (prx)


Lesenswert?

In Konstruktor und Destruktor schon. Da gilt das late binding nicht.

Abgesehen davon kann ich mir Optimierungen vorstellen, die das sinnvoll 
machen. Beispielsweise wenn per Profiling festgestellt wird, dass 95% 
der Aufrufe eine bestimmte Implementierung betreffen und der Compiler 
entsprechende Zweige einbaut.

von Rolf Magnus (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> goldeneyes1987 schrieb:
>> In wie weit macht die Kombination von virtual und inline einen Sinn?
>
> Die hat keinen Sinn, denn die tatsächlich zutreffende Methode kann ja
> bei late binding zwangsweise erst zur Laufzeit ermittelt werden.
> Damit ist es dem Compiler unmöglich, sie bereits zur Compile-Zeit
> inline einzufügen.

Sofern der Compiler an der  Stelle eines Aufrufs den tatsächlichen Typ 
erkennen kann, kann er das durchaus inlinen.

A. K. schrieb:
> Im Konstruktor schon. Da gilt das late binding nämlich nicht.

Doch, aber in seiner Klassenhierarchie ist es erst bis zu der Klasse, zu 
der der Konstruktor gehört, erzeugt, und deshalb arbeitet das late 
binding an der Stelle nicht weiter, als bis zu dieser Klasse.

von Karl H. (kbuchegg)


Lesenswert?

Auch dann, wenn der tatsächliche Datentyp bekannt ist, kann es sinnvoll 
sein eine virtual inline zu machen.

void foo()
{
  RadioControl i;  // an dieser Stelle haben wir es immer mit
                   // einer RadioControl zu tun

  i.fcn();
}

Der Compiler kann hier problemlos inlinen, selbst wenn die Funktion 
virtual ist.

von (prx) A. K. (prx)


Lesenswert?

Rolf Magnus schrieb:

> Doch, aber in seiner Klassenhierarchie ist es erst bis zu der Klasse, zu
> der der Konstruktor gehört, erzeugt, und deshalb arbeitet das late
> binding an der Stelle nicht weiter, als bis zu dieser Klasse.

Eben. Und da der Compiler an dieser Stelle deshalb genau weiss, welche 
Basisklasse welche virtuelle Methode selbst implementiert, kann er die 
direkt aufrufen. "late binding" ist das daher nicht.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A. K. schrieb:
> In Konstruktor und Destruktor schon. Da gilt das late binding nicht.

Stimmt natürlich.

von Rolf Magnus (Gast)


Lesenswert?

A. K. schrieb:
> Rolf Magnus schrieb:
>
>> Doch, aber in seiner Klassenhierarchie ist es erst bis zu der Klasse, zu
>> der der Konstruktor gehört, erzeugt, und deshalb arbeitet das late
>> binding an der Stelle nicht weiter, als bis zu dieser Klasse.
>
> Eben. Und da der Compiler an dieser Stelle deshalb genau weiss, welche
> Basisklasse welche virtuelle Methode selbst implementiert, kann er die
> direkt aufrufen. "late binding" ist das daher nicht.

Für Aufrufe direkt aus dem Konstruktor heraus kann der Compiler das 
natürlich entsprechend optimieren. Sobald du aber aus dem Konstruktor 
eine Funktion aufrufst, die dann wiederum eine virtuelle Membefunktion 
des Objekts aufruft, hast du sehr wohl late binding, aber eben nur bis 
zu der Klasse, zu der der  Konstruktor gehört.
Ich wollte damit nur sagen, daß late binding nicht etwa komplett 
abgeschaltet ist, nur wei das Objekt gerade erzeugt oder zerstört wird. 
Das wird nämlich oft fälschlicherweise angenommen. Hier noch ein 
Beispiel:
1
#include <iostream>
2
3
struct A
4
{
5
    void foo() { bar(); }
6
    virtual void bar() { std::cout << "A::bar()\n"; }
7
};
8
9
struct B : A
10
{
11
    virtual void bar() { std::cout << "B::bar()\n"; }
12
    B() { foo(); }
13
};
14
15
struct C : B
16
{
17
    virtual void bar() { std::cout << "C::bar()\n"; }
18
};
19
20
int main()
21
{
22
    C();
23
}
Die Ausgabe dieses Programms lautet
1
B::bar()

von cskulkw (Gast)


Lesenswert?

Was willst Du eigentlich bezwecken?

Willst Du den Polymorphismus benutzen, um aufgrund einer virtuellen 
Basisklasse ein Array anzulegen, dass auf Objekte unterschiedlicher 
abgeleiteter Klasse zeigen soll?

Ansonsten habe ich das Thema körperlose Klasse so verstanden, dass man 
in den abgeleiteten Klassen die Funktion mit Anweisungen versehen muß.

Das kann man aus Deinem Post nicht erkennen.

goldeneyes1987 schrieb:
> virtual void fcn() = 0;

Warum willst Du einer Funktion in der körperlosen Klasse eine Null 
zuweisen?
Hattest Du da im Hinterkopf, einen Funktionspointer mit 0 zu 
initialisieren. Wenn fcn hier ein Prototyp ist, dann empfiehlt sich eine 
void in den Klammern. Muß man nicht, hift aber ungemein.

Die Zuweisung eines Wertes auf einen Funktionsprototypen dürfte der 
Compiler nicht akzeptieren.


Hier mal mein Verständnis:
/*#abstrakte Klasse ###########################*/
class IDevice
{

/*Konstruktoren und destruktoren denkt ihr hier euch mal.
  Ansonsten gilt die default-Konst / Destruktor. */

  public:
     void ZeigMal(void);

}
/*#Klasse           ###########################*/
clase Bildschirm : IDevice
{

/*Konstruktoren und destruktoren denkt ihr hier euch mal.
  Ansonsten gilt die default-Konst / Destruktor. */

  public:
     void Zeigmal(void);
}
/*#Klasse           ###########################*/
class Lautsprecher
{
/*Konstruktoren und destruktoren denkt ihr hier euch mal.
  Ansonsten gilt die default-Konst / Destruktor. */

  public:
     void Zeigmal(void);
}
/*############################*/
void Bildschirm::Zeigmal(void)
{
   cout << "Ich bin ein Bildschirm\r\n";
}
/*############################*/
void Lautsprecher::Zeigmal(void)
{
   cout << "Ich bin ein Lautsprecher\r\n";
}

/* globales Array mit Typ abstrakte Basisklasse könnte
   auch in einer Klasse als private - Variable angegeben sein. */

IDevice* Liste[2];


int main(void)
{
   Liste[1] = new Bildschirm;
   Liste[2] = new Lautsprecher;

   Liste[1]->ZeigMal();
   Liste[2]->ZeigMal();

   /* dynamisch erzeugte Speichervariablen freigeben. */

}

Wenn jetzt main abgearbeitet wird, sollte Folgendes herauskommen.

Ich bin ein Bildschirm
Ich bin ein Lautsprecher

So habe ich das mit den abstrakten (körperlosen) Klassen verstanden und 
mehrfach mit der Umsetzung des Polymorphismus (Vielgesichtigkeit) 
erfolgreich eingesetzt.

Bitte nagelt mich jetzt nicht, wenn ich oben Fehler habe. Ich wollte nur 
das Prinzip zeigen...

von cskulkw (Gast)


Lesenswert?

Sorry habe das virtual vergessen. Meine OOP-Erfahrungen sind schon etwas 
angestaubt.


Hier mal mein Verständnis:
/*#abstrakte Klasse ###########################*/
class IDevice
{

/*Konstruktoren und destruktoren denkt ihr hier euch mal.
  Ansonsten gilt die default-Konst / Destruktor. */

  public:
     virtual void ZeigMal(void);

}
/*#Klasse           ###########################*/
clase Bildschirm : IDevice
{

/*Konstruktoren und destruktoren denkt ihr hier euch mal.
  Ansonsten gilt die default-Konst / Destruktor. */

  public:
     void Zeigmal(void);
}
/*#Klasse           ###########################*/
class Lautsprecher
{
/*Konstruktoren und destruktoren denkt ihr hier euch mal.
  Ansonsten gilt die default-Konst / Destruktor. */

  public:
     void Zeigmal(void);
}

von Karl H. (kbuchegg)


Lesenswert?

cskulkw schrieb:

> goldeneyes1987 schrieb:
>> virtual void fcn() = 0;
>
> Warum willst Du einer Funktion in der körperlosen Klasse eine Null
> zuweisen?

um sie als pure zu markieren.
'pure' ist wohl das, was du als 'körperlos' bezeichnest.

> class IDevice
> {
>
> /*Konstruktoren und destruktoren denkt ihr hier euch mal.
>   Ansonsten gilt die default-Konst / Destruktor. */
>
>   public:
>      void ZeigMal(void);
>
> }

Und wenn du diese Funkion jetzt auch noch pure machst,

      void ZeigMal(void) = 0;

dann lässt dich der Compiler kein Objekt der Klasse IDevice anlegen, 
eben weil die Klasse selbst dadurch pure gemacht wurde. De facto zwingt 
man dadurch eine davon abgeleitete Klasse, die Funktion zu 
implementieren. Tut man es nicht, dann ist auch die Ableitung wieder 
pure und man kann kein Objekt davon erzeugen. Erst durch das 
Implementieren der Funktion ZeigMal in Bildschirm wird die Klasse 
Bildschirm un-pure und man kann auch Bildschirmobjekte erzeugen.

Es läuft also darauf hinaus, dass eine Klasse von sich sagen kann: Du 
kannst von mir ableiten, aber wenn du das tust, dann MUSST du diese und 
jene Funktion implementieren.


> So habe ich das mit den abstrakten (körperlosen) Klassen verstanden und
> mehrfach mit der Umsetzung des Polymorphismus (Vielgesichtigkeit)
> erfolgreich eingesetzt.

Nur dass deine IDevice Klasse eben nicht formal abstrakt (=pure) ist. In 
C++ gibt es dafür ein Sprachmittel, mit dem man das auch so ausdrücken 
kann, dass es der Compiler überwachen kann. Und das ist immer besser, 
als wie wenn man sich auf Konventionen verlassen muss.

Übrigens bedeutet pure nicht, dass die Funktion keine Implementierung 
haben kann. Sie kann, aber sie muss nicht.


> Das kann man aus Deinem Post nicht erkennen.
Eigentlich kann man das sehr gut erkennen. Ist ein klassischer 
Polymorphie-Ansatz, der auf einer abstrakten Basisklasse als 
Interface-Beschreibung aufbaut.

von Frank (Gast)


Lesenswert?

Wow,  nicht eine  einzige brauchbare  Antwort



IDevice* testDevice[10];
RadioControl myDevice;

testDevice[0] = ( IDevice* ) &myDevice;

 wenn schon denn schon typecast

von Karl H. (kbuchegg)


Lesenswert?

Frank schrieb:
> Wow,  nicht eine  einzige brauchbare  Antwort
>

*) doch, gab es.
   ausführbare Anweisungen gehören in eine Funktion und nicht einfach
   nur irgendwie freistehend ausserhalb.

*) deine ist aber auch nicht brauchbarer:

> IDevice* testDevice[10];
> RadioControl myDevice;
>
> testDevice[0] = ( IDevice* ) &myDevice;
>
>  wenn schon denn schon typecast

No. Genau diesen Cast willst du nicht haben. Er ist komplett unnötig. 
Und unnötige Casts sind Zeitbomben.

Ein RadioControl IST EIN IDevice
1
class RadioControl : public IDevice
2
....

Mach noch einen Versuch.

von Εrnst B. (ernst)


Lesenswert?

Karl Heinz Buchegger schrieb:
> um sie als pure zu markieren.

Besser "pure virtual" (also "rein virtuell") oder "abstract" schreiben.

"pure" alleine verwechselt man sonst evtl mit dem 
"pure"-Funktionsattribut (==Seiteneffektsfrei), das der GCC z.B. für die 
Optimierung verwendet.

(Offtopic:

mit dem Prototyp
1
int my_strlen(char*) __attribute__ ((pure));

darf der Compiler dann z.B.
1
int x=my_strlen(a)+my_strlen(a);

als
1
int x=2 * my_strlen(a);

compilieren, und einen Funktionsaufruf wegsparen

)

von Karl H. (kbuchegg)


Lesenswert?

Εrnst B✶ schrieb:
> Karl Heinz Buchegger schrieb:
>> um sie als pure zu markieren.
>
> Besser "pure virtual" (also "rein virtuell") oder "abstract" schreiben.
>
> "pure" alleine verwechselt man sonst evtl mit dem
> "pure"-Funktionsattribut (==Seiteneffektsfrei), das der GCC z.B. für die
> Optimierung verwendet.

Merci.
Werds mir merken. Guter Punkt.

von Rolf M. (rmagnus)


Lesenswert?

Εrnst B✶ schrieb:
> Karl Heinz Buchegger schrieb:
>> um sie als pure zu markieren.
>
> Besser "pure virtual" (also "rein virtuell") oder "abstract" schreiben.

Memberfunktionen können rein virtuell sein. Eine Klasse, die solche hat, 
ist abstrakt.

von Martin (Gast)


Lesenswert?

cskulkw schrieb:
> körperlose Klasse

Google-Treffer: drei. Zwei religiöse und dein Posting.

Kannst du nicht einfach mal darauf verzichten, Begriffe selbst zu 
erfinden?

von Martin (Gast)


Lesenswert?

cskulkw schrieb:
>> virtual void fcn() = 0;
>
> Warum willst Du einer Funktion in der körperlosen Klasse eine Null
> zuweisen?
> Hattest Du da im Hinterkopf, einen Funktionspointer mit 0 zu
> initialisieren. Wenn fcn hier ein Prototyp ist, dann empfiehlt sich eine
> void in den Klammern. Muß man nicht, hift aber ungemein.

Lern doch einfach erstmal C++.

Das ist die ganz normale Syntax, um rein virtuelle Methoden zu 
vereinbaren.

Und ja, sie ist so gewählt worden, weil sie syntaktisch an die Zuweisung 
eines Nullpointers erinnern soll.

von Karl H. (kbuchegg)


Lesenswert?

Wenn wir schon beim nörgeln sind:

> Wenn fcn hier ein Prototyp ist, dann empfiehlt sich eine
> void in den Klammern. Muß man nicht, hift aber ungemein.

Nicht .... wirklich.

Anders als in C hat eine leere Argumentliste in C++ keine 
Sonderbedeutung. Die Funktion nimmt keine Argumente. Aus - Punkt. Egal 
ob da void drinnen steht oder nicht.

Was aber tatsächlich ein aus Programmierersicht wichtiger Punkt gewesen 
wäre:
1
class IDevice
2
{
3
  public:
4
     virtual void ZeigMal(void);
5
6
}
7
8
clase Bildschirm : IDevice
9
{
10
  public:
11
     void Zeigmal(void);
12
}

wenn eine Funktion in der Basisklasse virtual ist, dann ist es eine 
extrem gute Idee, sie auch in den abgeleiteten Klassen ebenfalls 
explizit als virtual zu markieren. Du musst es nicht tun, der Compiler 
vererbt das virtual aus einer Basisklasse automatisch an die jeweiligen 
Funktionen. Aber: Wenn sich das über mehrere Hierarchieebenen hinzieht, 
schwöre ich dir, dass du dich irgendwann nicht mehr auskennst, welche 
Funktionen jetzt virtual sind und welche nicht.

(Das ist auch einer meiner heftigsten Kritikpunkte an C++: Die 
Behandlung des virtual Keywords und das es kein Fehler ist, wenn in 
einer abgeleiteten Klasse das entsprechende virtual fehlt. Fehler an 
dieser Stelle können heftig ins Auge gehen und zu Problemen führen, die 
man nur sehr schwer findet)


> Was aber tatsächlich ein aus Programmierersicht wichtiger Punkt
> gewesen wäre:
Und natürlich, dass IDevice einen virtual Destruktor braucht. Da du CTor 
und DTor aber ausgespart hast, belasse ich es im Moment dabei.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.