Forum: PC-Programmierung C++ - explicit für Dummies


von Gast (Gast)


Lesenswert?

Hallo
ich plage mich im Moment mit dem explicit Schlüsselwort in C++.

Ich habe eine Klasse A als Datencontainer:
1
class A
2
{
3
public:
4
 A(void);
5
 explicit A(const A& other);
6
 int get() const;
7
 void set(int i);
8
... 
9
private:
10
 int i;
11
};

Und eine Klasse B, die mit diesen Daten arbeitet und sie zurückgeben 
soll:
1
class B
2
{
3
public:
4
A getData() const;
5
...
6
};
7
8
...
9
10
A B::getData() const
11
{
12
 A a;
13
 //Berechnungen mit a
14
 return a; //Das geht nicht??
15
}

Wieso kommt bei B::getData() eine Fehlermeldung?
Ich erzeuge nur eine Objekt, fülle es mit setter-Methode mit Daten
und liefere eine Kopie desselben zurück. Mir ist schon klar, dass
der explicit Copy-Ctor aufgerufen wird. Ich kann aber nicht sehen, wo
da eine implizite Typumwandlung statt findet.

Gruß
Gast

von Der A. (der-albi)


Lesenswert?

Das ist so ein Problem... hehe.
Also weder sagst du, um welchen Compiler es sich handelt, noch welche 
Fehlermeldung kommt.

Also der BorlandCompiler schluckt den Code. Wenn ich ihn verwende gibts 
natürlich Linker-Fehler.

Ich gehe mal davon aus, dass du all deine Methoden und Kontruktoren 
sowie Operatoren alle implementiert hast?

Also so gehts bei mir auch Ohne Linker-Fehler (ist auch sinnlos, aber 
den Funktionsinhalt kennst auch nur du)=

class A
{
public:
 A(void) { /**/ };
 explicit A(const A& other) { /**/ }
 int get() const;
 void set(int i);

private:
 int i;
};

von Gast (Gast)


Lesenswert?

Es handelt sich um den Microsoft Compiler (Visual Studio 2008).
Fehlermeldung:
 error C2558: class 'A': Kein Kopierkonstruktor verfügbar oder der 
Kopierkonstruktor is als 'explicit' deklariert

Ja, ich habe den Copy-Ctor und den Zuweisungsoperator implementiert.
Wenn ich die get()-Methode ändere, so dass ein Pointer auf ein dynamisch 
erstelltes Objekt geliefert wird gehts und ich kann mit diesem Objekt 
ohne Probleme arbeiten.

von MaWin (Gast)


Lesenswert?

Was soll das return a; machen ?

Das Objekt a liefern, welches du lokal mit A a; in der Funktion/Methode 
definiert hast ?

Geht ja nicht, das ist lokal, das ist weg wenn die Funktion verlassen 
wird. Das exisitiert nicht mehr, wenn von aussen jemand auf dieses als 
Funktionsergebnis zugreifen möchte.

Also versucht der Kompiler, das, was du von dem 'under the hood' nötigen 
Aufwand nicht siehst weil du die Programmierung noch nicht beherrscht, 
für dich geradezubiegen. Er legt ein unbenanntes Objekt vom Klassentyp A 
an, welches länger existiert - auch über den Aufruf der Funktion hinaus.
Dazu muss aber der Inhalt von A a; in das unbekannte A kopiert werden. 
Das wäre kein Problem, hast du doch einen Konstruktur der ein A aus 
einem A anfertigen kann, unter beibehaltung des Inhalts, extra 
definiert. Ja, es wird mit Arbeitsaufwand eine Kopie angelegt, ja, es 
gibt dann 2 Objekte der Klasse A, das lokale a in der Funktion und das 
Ergebnis-A an der Aufrufstelle, und ja, die Daten in A (also dein i) 
müssen kopiert werden damit das funktioniere kann, aber bei C++ kommt es 
nicht auf den Aufwand an, C++ unternimmt jeden Aufwand, um unüberleegtes 
vom Programmierer doch noch irgendwie hinzubiegen, man merkt es meist an 
der Langsamkeit der Programme.

Aber du verbietest es dem Kompiler, an dieser Stelle deinen Konstruktor 
A(const A& other); als Kopieroperator aufzufufen. Denn du hast ein 
'explicit' davorgeschrieben. Der darf nur aufgerufen werden, wenn du 
explizit ein A(a) hinschreibst.

Und nun wunderst du dich, warum der Compiler das nicht kompilert, warum 
er sich an dein Verbot hält und eine Fehlermeldung produziert?

Warum Borland das kompilert, ist mir schleierhaft. Borland C++ wird 
genau so dumm sein wie Borland C, welches auch eine char* f() { char 
c[2]={'!','\0'}; return c; } anstandslos kompilert, die dann manchmal 
funktioniert print("%c",*f()); und machmal nicht print("%s",f());

von Klaus W. (mfgkw)


Lesenswert?

MaWin schrieb:
> Was soll das return a; machen ?
>
> Das Objekt a liefern, welches du lokal mit A a; in der Funktion/Methode
> definiert hast ?
>
> Geht ja nicht, das ist lokal, das ist weg wenn die Funktion verlassen
> wird. Das exisitiert nicht mehr, wenn von aussen jemand auf dieses als
> Funktionsergebnis zugreifen möchte.

das ist hier kein Problem, weil A B::getData() const den Rückgabetyp A
hat und nicht A&.
Dadurch legt der Compiler ja eh ein temporäres Objekt an.
Für dessen Erzeugung braucht man allerdings dann den ctor, und der
scheint zu fehlen.

> ...

von MaWin (Gast)


Lesenswert?

> Dadurch legt der Compiler ja eh ein temporäres Objekt an.

Du hättest weiterlesen sollen, das wurde dann schon beschrieben,
dort wo du
> > ...
nicht mehr aufgepasst hast.

von Klaus W. (mfgkw)


Lesenswert?

Ich habe schon weiter gelesen.
"Beschwert" habe ich mich nur über den zitierten Teil, weil
der so nicht stimmt - hier liegt nicht das Problem.

Ich wollte nicht behaupten, daß der Rest falsch wäre; es kam mir so
aber mißverständlich vor. Sorry, falls das nicht rüberkam.

von Karl H. (kbuchegg)


Lesenswert?

Um ehrlich zu sein, kams mir auch beim ersten Durchlesen etwas 
missverständlich vor.

Die weiteren Ausführungen sind aber astrein formuliert.
Die Konklusio hätte man noch etwas deutlicher formulieren können:

Der Compiler darf, dank dem 'explicit' den Copy Construktor nur dann 
verwenden, wenn der Programmierer das ausdrücklich erlaubt, bzw. den 
Copy Construktor ausdrücklich anfordert.

Einen cctor explicit zu machen, mag vielleicht am Anfang der Karriere 
sinnvoll sein, mit ein wenig Erfahrung ist das aber eher sinnlos. Mit 
etwas Erfahrung, ist gerade der CCtor eigentlich überhaupt kein Problem. 
Wenn eine Kopie nötig ist, dann hilft es ohnehin nichts: Die Kopie muss 
erzeugt werden.

von MaWin (Gast)


Lesenswert?

> "Beschwert" habe ich mich nur über den zitierten Teil, weil
der so nicht stimmt

Was stimmt am "Geht ja nicht" nicht?

Du müsstest mal das Konzept der Verneinung erlernen.

von Karl H. (kbuchegg)


Lesenswert?

> Also versucht der Kompiler, das, was du von dem 'under the hood'
> nötigen Aufwand nicht siehst weil du die Programmierung noch nicht
> beherrscht, für dich geradezubiegen.

Mich stört das 'der Compiler versucht etwas geradezubiegen'.
Das tut er in dem Fall nicht.
Er versucht genau das zu implementieren, was da steht.
Der Returntype ist Objekt und da steht
   return a;

und genau das möchte der Compiler auch implementieren, wenn ihn nicht 
der explicit CCtor daran hindern würde.
In diesem Fall geht ausnahmsweise mal nichts Magisches under-the-hood 
ab.

von Ingo E. (ogni42)


Lesenswert?

>In diesem Fall geht ausnahmsweise mal nichts Magisches under-the-hood
>ab.

... sondern nur das, was im Standard steht:

"6.6.3, Abs 2: [...] A return statement can involve the construction and 
copy of a temporary object (12.2).[...]"

von Klaus W. (mfgkw)


Lesenswert?

@MaWin:
Ich hatte den Satz sogar weitergelesen als du. :-)
Da steht nicht nur: "Geht ja nicht"; das wäre korrekt.
Tatsächlich steht da: "Geht ja nicht, das ist lokal, das ist weg wenn 
die Funktion verlassen". Und diese Begründung fand ich halt falsch. Weil
der Rückgabewert keine Referenz ist, wäre das kein Problem.

Unbenommen der Tatsache, daß es danach wieder richtig ist.
Und unbenommen der Tatsache, daß wir jetzt vielleicht einfach aneinander 
vorbei reden....

Und wie gesagt, wollte ich dir gar nicht ans Bein pinkeln.

von Referenz (Gast)


Lesenswert?

gib das a als Referenz zurück

 A & getData() const
{
 A a;

 return a;
}

das geht auf jeden Fall

von Karl H. (kbuchegg)


Lesenswert?

Referenz schrieb:
> gib das a als Referenz zurück
>
>  A & getData() const
> {
>  A a;
>
>  return a;
> }
>
> das geht auf jeden Fall

Autsch!
Jetzt hast du erst recht ein Problem. Und dieses Problem lässt sich 
nicht mit dem entfernen des 'explicit' vom CCtor lösen.

von Gast (Gast)


Lesenswert?

lol, also die Arroganz mit der hier manche argumentieren, ist schon 
interessant. Naja, solange das Fachliche stimmt solls mir egal sein.
MaWin, ich denke du lehnst dich ein bißchen zu weit aus dem Fenster, 
wenn du pauschal behauptest, ich verstehe nichts vom Progammieren. Die 
Abläufe in der Methode sind mir durchaus bewusst(steht auch in meinem 
ersten Post). Ich konnte bis jetzt lediglich bestens auf das 
explicit-Schlüsselwort verzichten und wollte es lediglich mal 
ausprobieren. Aus einem Buch(Strasser, Programmieren mit Stil) habe ich 
in Erinnerung, dass explicit die implizite Typumwandlung verbietet. Dass 
es jeden impliziten Konstruktoraufruf verbietet, war mir bis heute nicht 
bewusst. Wie gesagt, ging bis jetzt auch ohne :P.

Gruß
Gast

von Klaus W. (mfgkw)


Lesenswert?

Es gibt viele Bücher über C++.
Im Stroustrup steht's drin.

Was ich an der ganzen Sache nicht verstehe ist: wieso schreibt am
an den copy-ctor überhaupt explicit dran?
Was hat man davon? Ich kann mir nicht denken, daß es mir irgendwann
mal irgendwie geholfen hätte.
Kann aber heute auch an meiner mangelnden Phantasie liegen, bin nicht
ganz fit.

von Ingo E. (ogni42)


Lesenswert?

Der Nutzen von explicit ist, dass Du implizite (da schau her :-) 
Typumwandlungen verhindern kannst.

Bsp.:
1
class A
2
{
3
public:
4
    A();
5
    explicit A(int& i);
6
    A(string& s);
7
};
8
9
A a;
10
a = 42; // error, implicit type conversion requested
11
a = "fubar"; // OK, implicit type conversion allowed

Korrigiert mich, wenn ich was falsch gemacht habe.

Warum man nun den CCTOR explicit machen sollte? Da fällt mir jetzt auch 
kein gutes Beispiel für ein. Höchstens um Fehler wie die Rückgabe einer 
Referenz auf eine stack-Variable wie im obigen Beispiel von "Referenz" 
dargestellt, zu verhindern.

von explicit_nicht_Braucher (Gast)


Lesenswert?

also doch eher esoterisch ;-)

von Rolf Magnus (Gast)


Lesenswert?

> Aus einem Buch(Strasser, Programmieren mit Stil) habe ich
> in Erinnerung, dass explicit die implizite Typumwandlung verbietet.
> Dass es jeden impliziten Konstruktoraufruf verbietet, war mir bis
> heute nicht bewusst.

Konstruktoren werden immer implizit aufgerufen, als Teil der Erzeugung 
von Objekten. Es ist schon richtig, daß 'explict' die impliziten 
Typumwandlungen verbietet. In C++ ist es so, daß JEDER Konstruktor, der 
mit genau einem Paramter aufgerufen werden kann, automatisch als 
Konvertierkonstruktor gilt. Das gilt auch für den Kopierkonstruktor. 
Quell- und Zieltyp müssen nicht verschieden sein, damit es in C++ als 
Konvertierung angesehen wird. 'explicit' sorgt nun dafür, daß ein 
Konvertierkonstruktor nicht für implizite Konvertierungen eingesetzt 
wird.

> also doch eher esoterisch ;-)

Eigentlich nicht. Ein typischer Anwendungsfall ist z.B. bei 
Container-Typen, die einen Konstruktor haben,  den man mit einem Integer 
als Parameter aufrufen kann, der die gewünschte (initiale) Größe angibt. 
Da kann es dann mit impliziten  Konvertierungen manchmal zu obskuren 
Problemen kommen, weil versteckte unerwartete Konvertierungen gemacht 
werden.  So wird dann z.B. jede Funktion, die einen solchen Container 
als Parameter akzeptiert, auf einmal auch einen int akzeptieren.  Der 
wird dann halt in einen Container der darin angegebenen Länge 
konvertiert, was meistens keinen Sinn ergibt. Sowas kann man mit 
'explicit' unterbinden.

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.