Hallo
ich plage mich im Moment mit dem explicit Schlüsselwort in C++.
Ich habe eine Klasse A als Datencontainer:
1
classA
2
{
3
public:
4
A(void);
5
explicitA(constA&other);
6
intget()const;
7
voidset(inti);
8
...
9
private:
10
inti;
11
};
Und eine Klasse B, die mit diesen Daten arbeitet und sie zurückgeben
soll:
1
classB
2
{
3
public:
4
AgetData()const;
5
...
6
};
7
8
...
9
10
AB::getData()const
11
{
12
Aa;
13
//Berechnungen mit a
14
returna;//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
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;
};
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.
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());
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.
> ...
> 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.
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.
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.
> "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.
> 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.
>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).[...]"
@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.
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.
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
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.
Der Nutzen von explicit ist, dass Du implizite (da schau her :-)
Typumwandlungen verhindern kannst.
Bsp.:
1
classA
2
{
3
public:
4
A();
5
explicitA(int&i);
6
A(string&s);
7
};
8
9
Aa;
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.
> 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.