問題
まずは下記のコードを示します.
shared_ptrを使ったコード
#include <iostream>
class BaseEntity {
public:
BaseEntity() {
std::cout << "BaseEntity" << std::endl;
}
BaseEntity(const BaseEntity&) {
std::cout << "BaseEntity(const BaseEntity&)" << std::endl;
}
virtual ~BaseEntity() = default;
virtual int getvalue() const noexcept { return 100; }
};
class DerivedEntity : public BaseEntity {
public:
DerivedEntity() {
std::cout << "DerivedEntity" << std::endl;
}
DerivedEntity(const DerivedEntity&) {
std::cout << "DerivedEntity(const DerivedEntity&)" << std::endl;
}
virtual ~DerivedEntity() = default;
int getvalue() const noexcept override { return -100; }
};
int main()
{
auto p = std::shared_ptr<BaseEntity>(new DerivedEntity());
std::cout << "value: " << p->getvalue() << std::endl;
}
実行結果は下記です.
BaseEntity
DerivedEntity
value: -100
派生クラス DerivedEntity を new してるのでオーバーライドされた getvalue 関数は正しく -100 を返してくれました. 期待通りです.
まぁこれでも良いのですが, どうやらメモリ効率と安全性から make_shared を使った方が良いんだぞ, というプラクティスがあることを思い出し書き直そうと思いました(やや疲れ気味の頭で).
make_sharedを使った誤ったコード
#include <iostream>
#include <memory>
class BaseEntity {
public:
BaseEntity() {
std::cout << "BaseEntity" << std::endl;
}
BaseEntity(const BaseEntity&) {
std::cout << "BaseEntity(const BaseEntity&)" << std::endl;
}
virtual ~BaseEntity() = default;
virtual int getvalue() const noexcept { return 100; }
};
class DerivedEntity : public BaseEntity {
public:
DerivedEntity() {
std::cout << "DerivedEntity" << std::endl;
}
DerivedEntity(const DerivedEntity&) {
std::cout << "DerivedEntity(const DerivedEntity&)" << std::endl;
}
virtual ~DerivedEntity() = default;
int getvalue() const noexcept override { return -100; }
};
int main()
{
auto p = std::make_shared<BaseEntity>(DerivedEntity());
std::cout << "value: " << p->getvalue() << std::endl;
}
BaseEntity
DerivedEntity
BaseEntity(const BaseEntity&)
value: 100
眠い目をこすって何度も確認しても何故か 100 が返されているようにしか見えません.
「そうだ, きっとこれは夢なんだ」
何が不味かったか
もう一度下記を見てみましょう.
shared_ptrを使ったコード
auto p = std::shared_ptr<BaseEntity>(new DerivedEntity());
make_sharedを使った誤ったコード
auto p = std::make_shared<BaseEntity>(DerivedEntity());
DerivedEntity()
で DerivedEntity がデフォルトコンストラクトされ, その後 BaseEntity のコピーコンストラクタに渡す, という動作になってしまっており, 意図しない有効なコード(コンパイラに怒られないという意味で)となってしまっていたのです.
派生クラスの shared_ptr を make_shared で作成するにはテンプレート引数として基底クラスではなく派生クラスを渡します.
なので, 下記のように記述すべきでした.
auto p = std::make_shared<DerivedEntity>();