アプリケーション開発ポータルサイト
ServerNote.NET
Amazon.co.jpでPC関連商品タイムセール開催中!
カテゴリー【C/C++
【C++】動的配列クラスstd::vectorにstd::shared_ptrを格納する
POSTED BY
2023-07-24

【C++】new/deleteを撤廃しstd::shared_ptrを使うの続きです。

newした生ポインタをvectorに格納する行為はSegmentation faultへの第一歩だが、shared_ptrはptrライクな生クラスなので問題なく格納できる。
shared_ptrは、自身を参照する人がゼロ人になったら保持しているクラスと自分自身を破棄=保持クラスのデストラクタが呼ばれる=のだが、この挙動はvectorのサンプルを書くのが一番確認しやすい。

C/C++vector.cppGitHub Source
#include <iostream>
#include <string>
#include <memory>
#include <vector>

class A {
  std::string mName;
public:
  A(void) {
    std::cout << "A constructor" << std::endl;
    mName = std::string("えーでふぉると");
  }
  A(std::string name) {
    std::cout << "A constructor" << std::endl;
    mName = name;
  }
  virtual ~A(void) {
    std::cout << "A destructor" << std::endl;
  }
  void hello(void) {
    std::cout << "A said こんにちは my name is" <<  mName << std::endl;
  }
};

void sample_1(void) {
  std::cout << "enter sample_1" << std::endl;

  std::vector<std::shared_ptr<A>> vec = std::vector<std::shared_ptr<A>>();

  std::shared_ptr<A> a1 = std::make_shared<A>(std::string("えーいち"));
  std::shared_ptr<A> a2 = std::make_shared<A>(std::string("えーに"));
  std::shared_ptr<A> a3 = std::make_shared<A>(std::string("えーさん"));
  std::shared_ptr<A> a4 = std::make_shared<A>(std::string("えーよん"));

  vec.push_back(a1);
  vec.push_back(a2);
  vec.push_back(a3);
  vec.push_back(a4);

  std::cout << "vector count=" << vec.size() << std::endl;

  vec.at(0)->hello();
  vec.back()->hello();

  std::cout << "before vector clear" << std::endl;
  vec.clear();
  std::cout << "after vector clear" << std::endl;

  std::cout << "leave sample_1" << std::endl;
}

void sample_2(void) {

  std::cout << "enter sample_2" << std::endl;

  std::vector<std::shared_ptr<A>> vec = std::vector<std::shared_ptr<A>>();

  vec.push_back(std::make_shared<A>(std::string("えーいち")));
  vec.push_back(std::make_shared<A>(std::string("えーに")));
  vec.push_back(std::make_shared<A>(std::string("えーさん")));
  vec.push_back(std::make_shared<A>(std::string("えーよん")));

  std::cout << "vector count=" << vec.size() << std::endl;

  vec.at(0)->hello();
  vec.back()->hello();

  std::cout << "before vector clear" << std::endl;
  vec.clear();
  std::cout << "after vector clear" << std::endl;

  std::cout << "leave sample_2" << std::endl;
}

int main(int argc, char **argv) {
  std::cout << "enter main" << std::endl;

  sample_1();

  sample_2();

  std::cout << "leave main" << std::endl;

  return 0;
}

関数sampleではクラスAの実体をshared_ptrで4つ確保(と同時に名前を与え)、shared_ptrポインタを動的配列クラスvectorに格納している。
shared_ptr破棄の挙動を確認する前に、C++で最も多用するstd::vectorの基本となる箇所を以下抜粋。

Aのshared_ptrを格納するvectorクラスの宣言・スタック変数として初期化確保

std::vector<std::shared_ptr<A>> vec = std::vector<std::shared_ptr<A>>();

配列の末尾に格納

vec.push_back(a1);

現在の要素数を取得する

std::cout << "vector count=" << vec.size() << std::endl;

任意のインデックスの要素を取得する。先頭を取得するには0。最後の要素を取得するにはbackを使うか、vec.at(vec.size() -1)とすればよい。

vec.at(0)->hello();
vec.back()->hello();

vectorをカラにするにはclearする。

vec.clear();

コンパイル、実行結果

g++ vector.cpp
./a.out

enter main
enter sample_1
A constructor
A constructor
A constructor
A constructor
vector count=4
A said こんにちは my name isえーいち
A said こんにちは my name isえーよん
before vector clear
after vector clear
leave sample_1
A destructor
A destructor
A destructor
A destructor
enter sample_2
A constructor
A constructor
A constructor
A constructor
vector count=4
A said こんにちは my name isえーいち
A said こんにちは my name isえーよん
before vector clear
A destructor
A destructor
A destructor
A destructor
after vector clear
leave sample_2
leave main

sample_1では、vec.clearとした段階では、vecはカラになるが、その場では4つあるクラスAはいずれも破棄されない(デストラクタが呼ばれていない)
これはsample1の関数内でスタック変数としてshared_ptr a1..a4を宣言確保しており、vecをカラにしてもこのsample1関数自体がまだa1..a4としてポインタを保有している=参照している人がまだ居るから=である。
sample_1関数を抜けると同時に、スタック変数a1..a4は廃棄されるので、ここで最後にAのデストラクタが呼ばれることになる。

対してsample_2では、スタック変数として宣言せず、vec.push_backに引数としてそのままmake_sharedして渡しているため、保有している人はvecただ1人である。よって今度は、vec.clearした時点で、もうAを参照している人はゼロになるため、その場でAのデストラクタが呼ばれshared_ptrが破棄されている様子が出力により確認できる。

C++公式日本語リファレンス
https://cpprefjp.github.io/reference.html

C++公式日本語リファレンス/std::shared_ptr
https://cpprefjp.github.io/reference/memory/shared_ptr.html

※本記事は当サイト管理人の個人的な備忘録です。本記事の参照又は付随ソースコード利用後にいかなる損害が発生しても当サイト及び管理人は一切責任を負いません。
※本記事内容の無断転載を禁じます。
【WEBMASTER/管理人】
自営業プログラマーです。お仕事ください!
ご連絡は以下アドレスまでお願いします★

☆ServerNote.NETショッピング↓
ShoppingNote / Amazon.co.jp
☆お仲間ブログ↓
一人社長の不動産業務日誌
【キーワード検索】