🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: de.comp.lang.c++
Article #1584

Aufruf einer Konstruktormethode aus einer anderen

#1584
From: Dietmar Kuehl
Date: Wed, 21 Feb 1996 12:09
113 lines
5014 bytes
[Ich habe (im Wesentlichen) diesen Artikel bereits am 17.2. geposted.
Allerdings habe ich ihn dannach ausser auf dem lokalen Newsserver
nirgends wiedergesehen... Daher poste ich ihn nochmal. Fuer den Fall,
dass er doch verteilt wurde, bitte ich um Entschuldigung.]

Hi,

In dem Thread mit dem Subject "Q: gcc-2.7.1: Aufruf einer
Konstruktormethode aus einer anderen" habe ich ganz klar behauptet,
dass man einen Konstruktor NICHT aus einem anderen Konstruktor aufrufen
kann [um das Objekt zu initialisieren; um ein temporaeres Objekt
anzulegen geht das natuerlich sehr wohl]. Hier ist ein Stueckchen Code,
von dem ich nicht sicher weiss, dass es portabel ist (ich werde das mal
in clc++m anfragen... Subject vermutlich "Constructor FAQ revisited"),
dass aber sehr wohl einen Konstruktor aus einem anderen aufruft, um das
Objekt zu konstruieren:

  #include <new>

  class foo
  {
  private:
    double bar;
  public:
    foo(double d):
      bar(d)
    {
    }
    foo(int i):
      bar(0.0) // dummy initialization; will be overridden anyway
    {
      this->~foo();
      new(this) foo(static_cast<double>(i));
    }
    ~foo()
    {
    }
  };

Das laesst sich problemlos mit gcc-2.7.2 compilieren und tut genau
das, was man erwartet.

Leider gibt es mit diesem Ansatz einige Probleme:
- wenn man den Konstruktor 'foo(int)' aus einer abgeleiteten Klasse
  aufruft, aendert sich im Konstruktor der Typ des Objekts. Das ist ein
  aehnliches Problem wie mit dem 'operator=(...)' der mittels
  Copy-Konstruktor implementiert wird: Eine abgeleitet Klasse muss den
  'operator=(...)' definieren und darf den 'operator=(...)' der
  Basisklasse nicht aufrufen. Aehnlich ist es hier: 'foo(int)' DARF aus
  einem Konstruktor einer abgeleiteten Klasse NICHT aufgerufen werden.
- es passiert eh nicht das, was man gerne haette: Vermeiden des
  Aufwands der vollstaendigen Konstruktion. Im Gegenteil wird sogar
  noch eine Destruktion benoetigt. Was man eigentlich gerne haette, was
  aber nicht geht ist etwa folgendes:

    foo(int i):  // geht nicht
      foo(static_cast<double>(i))
    {
    }

Fuer den unbedarften Leser sei an dieser Stelle nochmals erwaehnt, dass
folgendes NICHT das Objekt initialisiert, dass im Konstruktor
eigentlich initialisiert werden soll, sondern ein temporaeres Objekt
erzeugt:

  foo(int i):
    bar(0.0) // dummy initialization; will be overridden anyway
  {
    foo(static_cast<double>(i); // erzeugt temporaeres Objekt
  }

Was funktionieren wuerde, aber noch mehr Overhead als die placement-new
Variante hat (eine zusaetzliche Zuweisung) ist dies:

  foo(int i):
    bar(0.0) // dummy initialization; will be overridden anyway
  {
    *this = foo(static_cast<double>(i));
  }

Diese Implementation hat wenigstens den Vorteil, dass man von 'foo'
problemlos ableiten kann. Die, IMHO, besten Varianten um das Szenario in
den Griff zu bekommen sind folgende:
1. Eine (private) Member-Funktion, die von den verschiedenen
   Konstruktoren entsprechend aufgerufen wird.
2. Die entsprechende Konstruktion in ein Subobjekt (Member oder
   Basisklasse) verlagern: Indem man in dem "mem-initializer-list" den
   passenden Konstruktor aufruft, spart man ggf. eine Initialisierung
   die unmittelbar darauf ueberschrieben wird (zu dieser Methode bin
   ich in der Regel durch "natuerliche" Aenderungen am Klassendesign
   gekommen...).
Insbesondere von der Variante mit dem 'placement-new' rate ich ab, da
es mehr Probleme schafft, als loest.  Nicht umsonst antwortet z.B. auch
Steve Clamage auf die selbe Frage (nur in englisch gehalten) mit einem
kategorischen "geht nicht" (das ist genau das, wieso ich nicht sicher
bin, dass die Variante portable ist...). Siehe auch FAQ 18 vom
aktuellen clc++ FAQ (zu erreichen z.B.  unter
<http://www.informatik.uni-konstanz.de/~kuehl/cpp/cppfaq.htm#Q18>).

Bevor sich irgendwer darueber beklagt, dass ich zunaechst geantwortet
habe "geht nicht": Ich war mir bereits ueber den Ansatz mit dem
'placement-new' bewusst, halte ihn jedoch grundsaetzlich nicht fuer
eine Loesung. Auch ein Einwand der Form "Wenn es noetig ist, muss man
es halt verwenden" aendert daran nichts:  Das Design ist fuer'n A****,
wenn eine solche Konstruktion noetig ist! Man kann immer ueber den
Konstruktor eines Subobjekts gehen bzw. eine passende Funktion
schreiben.

dk
--
dietmar.kuehl@uni-konstanz.de
http://www.informatik.uni-konstanz.de/~kuehl
I am a realistic optimist - that's why I appear to be slightly pessimistic
Technical Details
Message-ID: <4gf220$f4u@news.BelWue.DE>
Path: arc1-ca.pugleaf.net!news.belwue.de!uzwil!kuehl

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!news.belwue.de!uzwil!kuehl
From: kuehl@uzwil.informatik.uni-konstanz.de (Dietmar Kuehl)
Newsgroups: de.comp.lang.c++
Subject: Aufruf einer Konstruktormethode aus einer anderen
Date: 21 Feb 1996 12:09:36 GMT
Organization: Fakultät für Mathematik und Informatik
Message-ID: <4gf220$f4u@news.BelWue.DE>
Reply-To: dietmar.kuehl@uni-konstanz.de
NNTP-Posting-Host: uzwil.informatik.uni-konstanz.de
X-Newsreader: TIN [version 1.2 PL2]