Saya menulis kode berikut yang menggunakan unique_ptr<Derived>
tempat yang unique_ptr<Base>
diharapkan
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;
}
dan saya berharap kode ini tidak dapat dikompilasi, karena menurut pemahaman saya unique_ptr<Base>
dan unique_ptr<Derived>
tidak terkait jenis dan unique_ptr<Derived>
sebenarnya bukan berasal dari unique_ptr<Base>
sehingga tugas tidak seharusnya bekerja.
Tetapi berkat beberapa sihir itu berhasil, dan saya tidak mengerti mengapa, atau bahkan jika itu aman untuk melakukannya. Bisakah seseorang menjelaskannya?
c++
templates
inheritance
unique-ptr
Youda008
sumber
sumber
unique_ptr
akan menjadi agak tidak berguna di hadapan warisanBase
tidak memiliki destruktor virtual.Jawaban:
Sedikit keajaiban yang Anda cari adalah konstruktor konversi # 6 di sini :
Ini memungkinkan untuk membangun secara
std::unique_ptr<T>
implisit daristd::unique_ptr<U>
if yang kedaluwarsa (mengabaikan batasan untuk kejelasan):Dengan kata lain, ini meniru konversi penunjuk mentah tersirat, termasuk konversi turunan ke basis, dan melakukan apa yang Anda harapkan ™ dengan aman (dalam hal masa pakai - Anda masih perlu memastikan bahwa tipe dasar dapat dihapus secara polimorfik).
sumber
Base
tidak akan memanggil destruktorDerived
, jadi saya tidak yakin apakah itu benar-benar aman. (Memang tidak kalah aman dari pointer mentah, diakui.)Karena
std::unique_ptr
memiliki konstruktor konversi sebagaidan
A
Derived*
dapat mengkonversiBase*
secara implisit, maka konstruktor yang dikonversi dapat diterapkan untuk kasus ini. Kemudian astd::unique_ptr<Base>
dapat dikonversi daristd::unique_ptr<Derived>
implisit seperti halnya pointer mentah. (Perhatikan bahwastd::unique_ptr<Derived>
harus menjadi nilai untuk membangunstd::unique_ptr<Base>
karena karakteristikstd::unique_ptr
.)sumber
Anda dapat secara implisit membangun
std::unique_ptr<T>
contoh dari nilai p daristd::unique_ptr<S>
setiap kaliS
bisa dikonversi keT
. Ini karena konstruktor # 6 di sini . Kepemilikan ditransfer dalam kasus ini.Dalam contoh Anda, Anda hanya memiliki nilai jenis
std::uinque_ptr<Derived>
(karena nilai pengembalianstd::make_unique
adalah nilai), dan ketika Anda menggunakannya sebagaistd::unique_ptr<Base>
, konstruktor yang disebutkan di atas dipanggil. Thestd::unique_ptr<Derived>
benda tersebut maka hanya hidup untuk waktu singkat, yaitu mereka diciptakan, maka kepemilikan diteruskan kestd::unique_ptr<Base>
objek yang digunakan lebih lanjut.sumber