今週の学習課題
- C++の学習
学習内容・結果
- スマートポインタ
- オブジェクト指向設計
- クラス図
- シーケンス図
- 配列型(std::list)
スマートポインタ
動的メモリは最後にdelete等で消す必要があるが、これは人的なミスで忘れてしまう可能性がある
スマートポインタは、メモリの所有権を与えてスコープが終了した段階、不要になった段階で自動的の解放させることが出来る。
スマートポインタには3種類ある
- unqiue_ptr
- shared_ptr
- weak_ptr
unqiue_ptr
一つのインスタンスが所有権を保持し、コピーは出来ないがムーブ(所有権の譲渡)は可能
std::unique_ptr<int> uptr1(new int(10));
std::unique_ptr<int> uptr2;
uptr2.reset(new int(20));
auto uptr3 = std::make_unique<std::string>("test");
auto uptr4 = std::make_unique<int,std::string>(40,"tedt");
std::cout<< *uptr3 <<std::endl; //「test」と表示
int usize = uptr2->size(); //関数の使用
bool cU = uptr1; //所有権の有無
auto utpr5 = std::make_unique<int>(50);
auto utpr6 = std::move(uptr5); //所有権の移動
shared_ptr
複数のインスタンスが所有権を保持する事が出来て、コピー、ムーブも可能。
基本的に宣言や関数の使い方は同じ。
unique_ptrとの大きな違いは所有権を複数のインスタンスが保持できる点
auto sptr1 = std::make_shared<int>(10);
std::make_shared<int> sptr2(sptr1); //「sptr1」をコピー
{
std::make_shared<int> sptr3;
sptr3 = sptr2; //「sptr2」をコピー
} //スコープを出たため「sptr3」は消える
//「sptr1」「sptr2」の所有権は残っている
ただし、同じメモリの所有権を保持するインスタンスが同時にデストラクタで解放しようとすると双方が解放時に他のインスタンスが保持している事になってしまい、メモリリークを起こしてしまう。
weak_ptr
unique_ptrやshared_ptrと違い所有権を保持せず、shared_ptrのメモリの参照する
auto sptr = std::make_shared<int>(10);
std::weak_ptr<int> wptr1(sptr);
std::weak_ptr<int> wptr2 = sptr;
std::weak_ptr<int> wptr3;
wptr3.reset(wptr1);
{
std::shared_ptr<int> ptr = wptr1.lock();
//一時的に所有権を取得
//解放されていた場合、空のshared_ptrを返す
}
weak_ptrで参照中に他のスレッド等の操作で参照元のshared_ptrのデストラクタが呼び出されてもメモリの所有権は消えず、weak_ptrがなくなることで完全にメモリの所有権がなくなる
unique_ptrとshared_ptrの使い分け
コピーが必要になると言う場面が少ないため、基本は「unique_ptr」が優先
「shared_ptr」を使うケース
- 複数のオブジェクトから同時にアクセスがある場合
- 使うオブジェクト達の中で最後まで所有するオブジェクトが不明な場合
- 使うオブジェクト達が全て終了した段階ですぐに解放したい場合
- 元のポインタが先に解放されてしまう場合
どのケースもマルチスレッドの場合であれば考えられる状況であり、次の案件でも使うことになりそうな内容でした。
オブジェクト指向設計
オブジェクト指向については復習でもあるため、重要な要素を簡素にまとめました
抽象化
スーパークラス以上でメソッドや変数を用意、把握しておくことで、末端のサブクラスの実装を知らなくとも、メソッドや変数を利用できる仕組み
カプセル化
クラスごとに所持する変数や関数を関連のあるものだけでまとめ、クラスごとに何を扱っているか分かりやすくする
情報隠匿
外部から使える変数、関数に対して制限を設ける。
制限を設けることで、クラス内にある意図しない関数(クラス内だけで使い回す処理等)や参照のみで上書きをさせたくない変数等へのアクセスを防ぐことが出来る
多態性
同じ型、同じ関数を使っても中のインスタンスによって実行内容が決まること
ただ扱いは「型」のクラスで扱うため、「型」にない関数は使えない。
継承
他のクラスを引き継ぐことで、元のクラスにあった変数や関数を利用することができ、複数のクラスが同じ関数を使う場合は、継承させることで何度も書く必要がなくなり、多態性、抽象化を実現するのに必要な書き方
クラス図
クラスが持つ変数や関数、他のクラスとの関係性(継承や使用)を線で表したもの
C++だからマルチスレッドの場合だからと言ったような特別今回から使うであろう内容ではありませんが、線や矢印の意味等を再度確認しました
シーケンス図
クラスやオブジェクト間のやり取りを時間軸で表した図
研修でもやったないようですが、今回C++のマルチスレッドを扱う上で、「並列処理」についてはしっかりと学び直すべきだと感じました。
並列処理「par」
同時に行われる処理を四角で囲み、処理ごとに点線で区切って表す
スレッドによる並列処理の表し方
クリティカルセッション「critical」
並列処理内の排他的制御を呼び出される側と呼び出している矢印を囲むように表す
「std::mutex」「condition_variable」を使って他の処理を待つ場合に使う
配列型(std::list)
標準テンプレートライブラリーの配列関連のクラス
配列の要素を先頭や最後尾に追加でき、イテレータを利用すると場所を指定して要素の追加、参照ができる
list<int> l;
l.push_back(1);
l.push_front(2);
//イテレータ
std::list<int>::itr;
itr = l.begin();
//イテレータにリストの先頭を設定
l.insert(itr,3);
std::cout<<*itr<<std::endl
イテレータは配列の要素のポインタを持っており、ポインタとして配列の要素にアクセスできる。
まとめ
今週で待機期間が明け、来月より次の現場での作業になります。
次の現場では、この期間中の学んだことが生かせるよう努力したいと考えております。