Frage

Ich habe das folgende erfundene Beispiel (aus dem echten Code):

template <class T>
class Base {
public:
 Base(int a):x(a) {}
    Base(Base<T> * &other) { }
    virtual ~Base() {}
private:
 int x;
};

template <class T>
class Derived:public Base<T>{
public:
  Derived(int x):Base<T>(x) {}
  Derived(Derived<T>* &other): Base<T>(other) {}

};


int main() {
 Derived<int> *x=new Derived<int>(1);
 Derived<int> y(x);
}

Wenn ich versuche, dies zu kompilieren, bekomme ich:

1X.cc: In constructor ‘Derived<T>::Derived(Derived<T>*&) [with T = int]’:
1X.cc:27:   instantiated from here
1X.cc:20: error: invalid conversion from ‘Derived<int>*’ to ‘int’
1X.cc:20: error:   initializing argument 1 of ‘Base<T>::Base(int) [with T = int]’

1) GCC wird eindeutig von den Konstruktoren verwirrt. Wenn ich die Referenz aus den Konstruktoren entferne, kompiliert der Code. Meine Annahme ist also, dass etwas mit aufstrebenden Zeigerreferenzen schief geht. Kann mir jemand sagen, was hier los ist?

2) Eine etwas unabhängige Frage. Wenn ich im Konstruktor etwas Schreckliches wie "Anderes" machen würde (mit mir), was passiert dann, wenn mir jemand einen Zeiger auf etwas auf dem Stapel weitergibt?

E.g. Derived<int> x(2);
     Derived<int> y(x);

where 

 Derived(Derived<T>*& other) { delete other;}

Wie kann ich sicherstellen, dass dieser Zeiger zu Recht auf etwas auf dem Haufen zeigt?

War es hilfreich?

Lösung

Base<T> ist eine Basistyp von Derived<T>, aber Base<T>* ist keine Basistyp von Derived<T>*. Sie können einen abgeleiteten Zeiger anstelle eines Basiszeigers übergeben, können jedoch keine abgeleitete Zeigerreferenz anstelle einer Basiszeigerreferenz übergeben.

Der Grund dafür ist, dass Sie angenommen haben, Sie könnten und vermuten, dass der Konstruktor der Basis einen Wert in die Referenz schreiben konnte:

Base(Base<T> * &other) {
    Base<T> *thing = new Base<T>(12);
    other = thing;
}

Sie haben gerade einen Zeiger auf etwas geschrieben, das ist nicht a Derived<T>, in einen Zeiger auf Derived<T>. Der Compiler kann dies nicht zulassen.

Andere Tipps

  1. Sie können keinen Verweis auf einen Zeiger in einen Verweis auf einen Zeiger auf die Basis konvertieren. (Vorlagen tragen hier nicht zum Thema bei, also entfernt von meinem Beispiel unten.)
  2. Wenn Sie die Verantwortung für einen Zeiger verschieben möchten, verwenden Sie einen intelligenten Zeigertyp. Intelligente Zeigertypen können die "Verantwortung für das Löschen" darstellen, die Rohzeiger nicht können. Beispiele sind std :: auto_ptr und Boost :: Shared_ptr, unter vielen anderen.

Warum Sie keine zierlichen Referenzen auflösen können:

struct Base {};
struct Derived : Base {};
struct Subclass : Base {};

int main() {
  Derived d;
  Derived* p = &d;
  Derived*& d_ptr = p;

  Base*& b_ptr = d_ptr; // this is not allowed, but let's say it is

  Base b;
  b_ptr = &b; // oops! d_ptr no longer points to a Derived!

  Subclass s;
  b_ptr = &s; // oops! d_ptr no longer points to a Derived!
}

Wenn Sie Ihren "anderen" Parameter an den Basis -CTOR übergeben, versuchen Sie, dasselbe zu tun wie b_ptr = d_ptr Oben.

Sie stellen sicher, dass der Zeiger auf etwas auf dem Haufen zeigt, indem Sie dies in Ihrer Dokumentation schreiben und sich darauf verlassen, dass der Anrufer das einhält. Wenn jeder, der Ihren Konstruktor anruft, einen Stapelzeiger übergibt, sind alle Wetten ausgeschaltet und es ist nicht Ihre Schuld - Sie können versuchen, das Problem frühzeitig zu erfassen, sondern keine Garantien.

So funktioniert die Standardbibliothek - oft wird sie offensichtliche Fehler aufnehmen, aber es ist nicht erforderlich, und es liegt an dem Anrufer, sicherzustellen, dass sie nichts Dummes tun.

Dein x Variable ist kein Zeiger, sondern sollte sein, wenn Sie a zuweisen möchten new Derived<int> dazu.

Wenn Sie Dinge auf dem Stapel löschen, tun Sie es nicht. Es gibt keine Möglichkeit zu sagen, ob Sie die Adresse von etwas auf dem Stapel oder auf dem Haufen übergeben haben (in der Tat erkennt der C ++ - Standard die Existenz eines Stapels nicht einmal an). Die Lektion hier ist, dass Sie keine Dinge löschen sollten, die Sie nicht besitzen, besonders wenn Sie nicht sagen, woher sie kommen.

Ich bin mir nicht sicher, warum Sie einen Hinweis auf den Zeiger haben möchten. Warum nicht

Base(Base<T> * other) { }

und

Derived(Derived<T>* other): Base<T>(other) {}

Das sollte funktionieren.

Und wie andere geantwortet haben, glaube ich nicht, dass Sie zu Recht wissen können, ob der Zeiger in den Haufen zeigt.

Bearbeiten: Warum kann man nicht tun, was Sie versuchen zu versuchen: Beispiel: Beispiel:

Derived1<int> *x = new Derived1<int>
Base<int> **xx =&x;
Derived2<int> y;
*xx = &y;

Wo abgeleitet1 und abgeleitet2 sind anders Klassen abgeleitet von der Basis? Würden Sie denken, dass es legitim ist? Nun dieser x vom Typ abgeleitet1* zeigt auf abgeleitet2?

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top