LoginSignup
9
9

More than 5 years have passed since last update.

shared_ptr を make_shared に何も考えずに置き換えようとして嵌った話

Last updated at Posted at 2017-10-28

問題

まずは下記のコードを示します.

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>();

参考

How to make_shared a derived class?

9
9
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
9