Ich habe den folgenden Code geschrieben, der verwendet, unique_ptr<Derived>
wo a unique_ptr<Base>
erwartet wird
class Base {
int i;
public:
Base( int i ) : i(i) {}
int getI() const { return i; }
};
class Derived : public Base {
float f;
public:
Derived( int i, float f ) : Base(i), f(f) {}
float getF() const { return f; }
};
void printBase( unique_ptr<Base> base )
{
cout << "f: " << base->getI() << endl;
}
unique_ptr<Base> makeBase()
{
return make_unique<Derived>( 2, 3.0f );
}
unique_ptr<Derived> makeDerived()
{
return make_unique<Derived>( 2, 3.0f );
}
int main( int argc, char * argv [] )
{
unique_ptr<Base> base1 = makeBase();
unique_ptr<Base> base2 = makeDerived();
printBase( make_unique<Derived>( 2, 3.0f ) );
return 0;
}
und ich erwartete diesen Code nicht kompilieren, weil nach meinem Verständnis unique_ptr<Base>
und unique_ptr<Derived>
nicht verwandte Arten sind und unique_ptr<Derived>
ist nicht in der Tat , die von unique_ptr<Base>
so die Zuordnung nicht funktionieren soll.
Aber dank etwas Magie funktioniert es und ich verstehe nicht warum oder auch wenn es sicher ist. Kann mir bitte jemand erklären?
c++
templates
inheritance
unique-ptr
Youda008
quelle
quelle
unique_ptr
wäre, wäre es in Gegenwart von Vererbung eher nutzlosBase
es keinen virtuellen Destruktor gibt.Antworten:
Das bisschen Magie, das Sie suchen, ist der Konvertierungskonstruktor Nr. 6 hier :
Es ermöglicht das
std::unique_ptr<T>
implizite Konstruieren eines aus einem ablaufendenstd::unique_ptr<U>
if (Beschönigen von Deletern zur Klarheit):Das heißt, es ahmt implizite Rohzeigerkonvertierungen nach, einschließlich Konvertierungen von abgeleiteten zu Basen, und führt das, was Sie erwarten, sicher aus (in Bezug auf die Lebensdauer - Sie müssen weiterhin sicherstellen, dass der Basistyp polymorph gelöscht werden kann).
quelle
Base
wird den Destruktor von nicht anrufenDerived
, daher bin ich mir nicht sicher, ob es wirklich sicher ist. (Es ist zugegebenermaßen nicht weniger sicher als roher Zeiger.)Da
std::unique_ptr
hat ein konvertierender Konstruktor alsund
A
Derived*
könnteBase*
implizit in konvertieren , dann könnte der konvertierende Konstruktor für diesen Fall angewendet werden. Dannstd::unique_ptr<Base>
könnte astd::unique_ptr<Derived>
implizit von einem konvertiert werden, genau wie der Rohzeiger. (Beachten Sie, dass das aufgrund der Eigenschaft vonstd::unique_ptr<Derived>
ein r-Wert für die Konstruktion sein muss .)std::unique_ptr<Base>
std::unique_ptr
quelle
Sie können eine Instanz implizit
std::unique_ptr<T>
aus einem Wert vonstd::unique_ptr<S>
wannS
immer konvertierbar ist erstellenT
. Dies liegt an Konstruktor Nr. 6 hier . In diesem Fall geht das Eigentum über.In Ihrem Beispiel haben Sie nur r-Werte vom Typ
std::uinque_ptr<Derived>
(da der Rückgabewert vonstd::make_unique
ein r- Wert ist), und wenn Sie diesen als verwendenstd::unique_ptr<Base>
, wird der oben erwähnte Konstruktor aufgerufen. Diestd::unique_ptr<Derived>
fraglichen Objekte leben daher nur für eine kurze Zeit, dh sie werden erstellt, und dann wird das Eigentum an dasstd::unique_ptr<Base>
Objekt weitergegeben, das weiter verwendet wird.quelle