2023-07-24
【C++】new/deleteを撤廃しstd::shared_ptrを使うの続きです。
newした生ポインタをvectorに格納する行為はSegmentation faultへの第一歩だが、shared_ptrはptrライクな生クラスなので問題なく格納できる。
shared_ptrは、自身を参照する人がゼロ人になったら保持しているクラスと自分自身を破棄=保持クラスのデストラクタが呼ばれる=のだが、この挙動はvectorのサンプルを書くのが一番確認しやすい。
C/C++ | vector.cpp | GitHub 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
※本記事内容の無断転載を禁じます。
ご連絡は以下アドレスまでお願いします★
☆ServerNote.NETショッピング↓
ShoppingNote / Amazon.co.jp
☆お仲間ブログ↓
一人社長の不動産業務日誌
TSUKUMOのマルチGPUパソコンWA9J-X211/XTのUbuntu Server 22.04でMYCOEIROINK作成コードを実行し音声合成モデルをトレーニングする
TSUKUMOのマルチGPUパソコンWA9J-X211/XTにUbuntu Server 22.04とCOEIROINK Engineを導入する
Python venv(仮想環境作成)でError: Command returned non-zero exit status 1のエラー対処法
【Ubuntu】プロジェクトごとに複数のPythonバージョンを使い分ける
【PHP】フォーム入力を受け取り出力する
【Ubuntu】aptでインストールしたapache2にソースコンパイルしたphpをマルチスレッドモードで組み込む
【PHP】HTMLファイルを読み込んで文字列変換して出力する
TSUKUMOのマルチGPUパソコンWA9J-X211/XTにUbuntu Server 22.04とVOICEVOX Engineを導入する
【VOICEBOX Engine】403 Forbidden Origin not allowedの対処法
【Windows10】リモートデスクトップ間のコピー&ペーストができなくなった場合の対処法
Googleファミリーリンクで子供の端末の現在地がエラーで取得できない場合
Windows版Google Driveが使用中と言われアンインストールできない場合
【C/C++】小数点以下の切り捨て・切り上げ・四捨五入
Windows11+WSL2でUbuntuを使う【2】ブリッジ接続+固定IPの設定
Googleスプレッドシートを編集したら自動で更新日時を入れる
Androidホームで左にスワイプすると出てくるニュース共を一切表示させない方法
【Apache】サーバーに同時接続可能なクライアント数を調整する
OpenAI API使用料金が事前支払い(チャージ)制になった【注意点】