質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
デザインパターン

デザインパターンは、ソフトウェアのデザインでよく起きる問題に対して、解決策をノウハウとして蓄積し再利用出来るようにした設計パターンを指します。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

3739閲覧

C++ シングルトン化がうまくいかない...メモリリークしてしまう。

UE4benkyo-

総合スコア37

デザインパターン

デザインパターンは、ソフトウェアのデザインでよく起きる問題に対して、解決策をノウハウとして蓄積し再利用出来るようにした設計パターンを指します。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

2クリップ

投稿2021/07/16 01:31

編集2021/07/16 02:29

こんにちは。

C++でシングルトンを勉強したので使おうと思ったのですが使ってみるとメモリリークしてしまいます。
イメージ説明
静的なメンバはポインタで扱っています。
一応ユニークポインタでやると当たり前ですがメモリリークしませんでした。

そして結果的にわからないところがいくつかあります。
・ポインタでやるとなぜメモリリークするのか?...書き方間違ってる?

・ユニークポインタのインスタンスを作るときにmake_uniqueだとできないのに
reset(new GameData_unique())だとできるのか?なにが違う?

・ポインタとユニークポインタでやりましたがシングルトンの静的な変数はポインタとかユニークポインタやまだ知らないものがあると思いますが何を使うのがいいのでしょうか?

以上です。
わかる方いましたら教えてほしいです。

以下はコードです。
なにかありましたら連絡ください。

// 2行目で書いてたやつです。下にも書いてありますが、一応。 s_gamedata_unique.reset(new GameData_unique()); 下のほうでやるとコンパイルエラーが出てしまいます。 C2248 'GameData_unique::GameData_unique': private メンバー (クラス 'GameData_unique' で宣言されている) にアクセスできません。 singrutest C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.24.28314\include\memory 2054 //s_gamedata_unique = std::make_unique<GameData_unique>();

C++

1GameData.h=================================== 2/* 3 ポインタのシングルトン 4*/ 5#pragma once 6 7 8class GameData 9{ 10private: 11 12 // コンストラクタ 13 GameData() 14 : num() 15 {} 16 17 // なにかしらの値を保存する変数 18 float num; 19 20public: 21 22 // インスタンスを生成 23 static GameData* GetInstans() 24 { 25 // 生成されてないとき生成 26 if (s_gamedata == nullptr) 27 { 28 s_gamedata = new GameData(); 29 } 30 31 // インスタンスを返す 32 return s_gamedata; 33 } 34 35 // num のセッタゲッタ 36 void SetNum(float num) { this->num = num; } 37 float GetNum() { return num; } 38 39private: 40 41 // 静的メンバを宣言 42 static GameData* s_gamedata; 43 44}; 45============================================== 46 47GameData.cpp=================================== 48#include "GameData.h" 49 50// 初期化 51GameData* GameData::s_gamedata = nullptr; 52 53============================================== 54

C++

1GameData_unique.h============================================= 2 3/* 4 ユニークポインタのシングルトン 5*/ 6#pragma once 7 8#include <memory> 9 10 11 12class GameData_unique 13{ 14private: 15 16 // コンストラクタ 17 GameData_unique() 18 : num() 19 {} 20 21 // なにかしらの値を保存する変数 22 float num; 23 24public: 25 26 // インスタンスを生成 27 static GameData_unique* GetInstans() 28 { 29 // 生成されてないとき生成 30 if (s_gamedata_unique == nullptr) 31 { 32 s_gamedata_unique.reset(new GameData_unique()); 33 //s_gamedata_unique = std::make_unique<GameData_unique>(); 34 } 35 36 // インスタンスを返す 37 return s_gamedata_unique.get(); 38 } 39 40 // num のセッタゲッタ 41 void SetNum(float num) { this->num = num; } 42 float GetNum() { return num; } 43 44private: 45 46 // 静的メンバを宣言 47 static std::unique_ptr<GameData_unique> s_gamedata_unique; 48 49}; 50================================================================ 51 52GameData_unique.cpp============================================= 53#include "GameData_unique_ptr.h" 54 55// 初期化 56std::unique_ptr<GameData_unique> GameData_unique::s_gamedata_unique = nullptr; 57================================================================ 58

c++

1#include <crtdbg.h> 2#include <iostream> 3 4#include "GameData.h" // ポインタ 5#include "GameData_unique_ptr.h" // ユニークポインタ 6 7 8 9int main() 10{ 11 _CrtSetDbgFlag(_CRTDBG_LEAK_CHECK_DF | _CRTDBG_ALLOC_MEM_DF); 12 13 14 std::cout << "ポインタ版のシングルトン============================" << std::endl; 15 16 // インスタンスを生成(ポインタ版) 17 GameData* gm = GameData::GetInstans(); 18 19 // 中身を表示 20 std::cout << gm->GetNum() << std::endl; 21 22 // 3.0fをセットする 23 gm->SetNum(3.0f); 24 25 // 中身を表示 26 std::cout << gm->GetNum() << std::endl; 27 28 29 std::cout << "ユニークポインタ版のシングルトン============================" << std::endl; 30 31 // インスタンスを生成(ユニークポインタ版) 32 GameData_unique* gm_unique = GameData_unique::GetInstans(); 33 34 // 中身を表示 35 std::cout << gm_unique->GetNum() << std::endl; 36 37 // 3.0fをセットする 38 gm_unique->SetNum(3.0f); 39 40 // 中身を表示 41 std::cout << gm_unique->GetNum() << std::endl; 42}

実行結果
イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

・ポインタでやるとなぜメモリリークするのか?

C++

1 static GameData* GetInstans() 2 { 3 // 生成されてないとき生成 4 if (s_gamedata == nullptr) 5 { 6 s_gamedata = new GameData(); 7 } 8 9 // インスタンスを返す 10 return s_gamedata; 11 }

ここで生成された GameData は delete されないので

投稿2021/07/16 02:19

episteme

総合スコア16612

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

UE4benkyo-

2021/07/16 02:43 編集

回答ありがとうございます。 確かに消さないとだめでしたね... こういう感じで毎回呼ぶでOKですか? // インスタンスを削除 void DeleteInstans() { delete s_gamedata; s_gamedata = nullptr; }
episteme

2021/07/16 03:41

毎回deleteしてシングルトンの要件を満たしますか?
UE4benkyo-

2021/07/16 14:08

満たしてないですね。ユニークポインタでやれば大丈夫そうですか?
episteme

2021/07/17 02:58

やってみりゃいぃじゃん。コンストラクタ/デストラクタでそれぞれ”生まれたよ”/”死んだよ”ってメッセージ吐かせ、それぞれ一回出力されればOKちゃうの? そのくらい自分でおやんなさい。
guest

0

ベストアンサー

・ポインタでやるとなぜメモリリークするのか?...書き方間違ってる?

newしたものを,誰もdeleteしないのですから,当然の結果ではないでしょうか.

・ユニークポインタのインスタンスを作るときにmake_uniqueだとできないのに

reset(new GameData_unique())だとできるのか?なにが違う?

GameData_uniqueのコンストラクタがprivateなので,make_uniqueGameData_unique型のオブジェクトを作ることができません.
対して,resetを使っている側だと,作っているのはメンバ関数であるGetInstansの中なのでOK.

・ポインタとユニークポインタでやりましたがシングルトンの静的な変数はポインタとかユニークポインタまだ知らないものあると思いますが何を使うのがいいのでしょうか?

良いかどうかはわかりませんが,こんなのもあり得るのでは.

C++

1class A 2{ 3public: 4 static A& GetInstance() //※「インスタンス」の綴りはこうでは? 5 { 6 static A Inst; //これが唯一のインスタンス 7 return Inst; 8 } 9 10private: 11 A(){} 12 //※コピーを禁止する必要がある 13 A &operator=( const A& ) = delete; 14 A( const A& ) = delete; 15};

投稿2021/07/16 02:15

編集2021/07/16 02:20
fana

総合スコア11996

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

UE4benkyo-

2021/07/16 02:41 編集

回答ありがとうございます。確かに当たり前でしたね... ではこんな感じでインスタンスを削除する関数を作って使い終わったときに毎回呼べば大丈夫そうですか? // インスタンスを削除 void DeleteInstans() { delete s_gamedata; s_gamedata = nullptr; } それからすみません。この部分は何を行っているのですか? A &operator=( const A& ) = delete; A( const A& ) = delete;
fana

2021/07/16 02:47

まぁ,「必ず誰かが DeleteInstans() を呼ぶこと」っていうルールにしたいならすれば良いと思うけど, それを自動でやるのがスマートポインタなわけで,あえて素のポインタ側でそういう話の物にするのは不便なだけかな. コピーの禁止については「C++ コピー 禁止」とか何とかでググった方が早いかと. 何もしないと,利用側で A OhterInstance = A::GetInstance(); とかすれば,新しいインスタンスを作れてしまいます.
fana

2021/07/16 02:52

void Func( A a ){ ... } //引数は値渡し! とかいう関数をつくって Func( A::GetInstance() ); //ヒャッハー! コピーだあ! とか呼んでやるとか,とにかくやりたい放題. シングルトンとは何だったのか?
fana

2021/07/16 02:59

シングルトンにするってことは, とにかくインスタンスが2つできちゃった時点でマジ無理! みたいな相応の危険物なハズだから,とにかくコピーとか作られたらまずいわけで. (逆に言えば,別にインスタンスが複数作られたところでどうということはない,というものであればシングルトンとかいう話を持ち込む必要もない)
UE4benkyo-

2021/07/16 03:02

わかりました。コピー禁止の方は後で調べてみますね。 つまり私のやつがシングルトンになっていないってことですかね?
fana

2021/07/16 03:06

シングルトンとは「とにかく絶対にインスタンスが1つしか生成されないことを保証するぜ」ってことでしょうから,それができていない(コピーを生成できるという抜け穴がある)という状況ですね.
UE4benkyo-

2021/07/16 03:12

ではきちんとしたシングルトンにするには勝手に消えるユニークポインタでなくずっと残る実体にして コピー禁止の処理を書けばよさそうですか?
fana

2021/07/16 03:41

> 勝手に消える っていうけど,それこそが unique_ptr とかの目的なんだから, 「消えてくれる(自動でdeleteしてくれる)」タイミングが所望の通りであれば良く, staticなメンバが解体されるのはプログラムが終わるときだろうから,そのタイミングで後始末されるのは目的に叶っているのでは. (なんかそういうの複数あって後始末の順序が重要だ,とかいう場合は自動だと困るのかも?)
UE4benkyo-

2021/07/16 14:07

つまり結論としてはユニークポインタでやっていい、それからコピー禁止の処理を書く、 という感じでよろしいですか?
fana

2021/07/17 00:37 編集

いいんじゃない? (…って私が思ってるから→そういう話を書いている.であれば,内容の正否確認を取るならば私でない第三者を相手取る必要があるような気がするけども)
UE4benkyo-

2021/07/17 07:28

今回はその方法で実装させていただきます。 回答ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問