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

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

新規登録して質問してみよう
ただいま回答率
85.46%
C++

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

Q&A

解決済

4回答

2969閲覧

複数クラスからアクセスする共通クラスを実装したい

tkym_1231

総合スコア57

C++

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

0グッド

0クリップ

投稿2021/08/31 01:37

編集2021/08/31 03:59

##環境
環境は、以下の通りです。
言語:c++
環境:VisualStudio2019

##前提
以下のように、ClassAの中に、
テキストファイルをopenし、文字を書き込んで、closeするTxtWriterクラスがあるとします。

c++

1 2class ClassA{ 3public: 4 //! コンストラクタ 5 //! m_pTxtWriterを実体化し、テキストファイルをopenする 6 ClassA(); 7 //! デストラクタ 8 //! テキストファイルをcloseする 9 ~ClassA(); 10 11 //! 処理 12 //! この関数の中でm_pTxtWriterを用いてテキストに書き込む 13 void Process(const short* input, short* output); 14 15private: 16 //! テキスト書き込みクラス 17 TxtWriter* m_pTxtWriter; 18}; 19 20 21/**********************************************/ 22//! TxtWriterクラス 23/**********************************************/ 24//! コンストラクタ 25TxtWriter::TxtWriter(std::string fname, std::string type, char delimiter) : m_sType(type), m_cDelimiter(delimiter) 26{ 27 // 開かなかったらエラーで終了 28 if(fopen_s(&m_fp, fname.c_str(), "w") != 0) 29 { 30 printf("Error! %s can not be opened", fname.c_str()); 31 exit(1); 32 } 33} 34 35//! デストラクタ 36TxtWriter::~TxtWriter() 37{ 38 fclose(m_fp); 39} 40 41//! データを1行書き込み 42template <typename T> 43void TxtWriter::WriteLine(const T* data, const int data_num) 44{ 45 std::string type = m_sType+"%c"; 46 for(int i=0; i<data_num-1; i++) 47 { 48 fprintf_s(m_fp, type.c_str(), data[i], m_cDelimiter); 49 } 50 fprintf_s(m_fp, type.c_str(), data[data_num-1], '\n'); 51}

##やりたいこと
インスタンス化された複数のclassAで、m_pTxtWriterを共通で使用したい。

##質問
m_pTxtWriterをコンストラクタで実体化すると、それぞれのclassAで実体を持つことになるので、上記の実装は良くないということは分かるのですが、どのように実装したら良いのか分かりません。

良い実装方法を教えていただけないでしょうか。

##補足
皆さま、ご回答をありがとうございます。説明不足な点がありましたので、補足させていただきます。

classAの仕様として、
・TxtWriterクラスはclassAの所有物である。
・classAを使う人は、classAのProcess関数を呼んだらテキストに出力されることを期待している
(classAを使う人にTxtWriterクラスを準備させたくない)
・複数のclassAインスタンスの出力先は固定
という観点から、外部から与えるのではなく、

ClassAのstaticなメンバ変数としてTxtWriterの実体または参照先(単純ポインタやstd::shared_ptr<TxtWriter>等)を保持する。

という方法が良いかなと考えました。
もし上記の方法で実装する場合、
・TxtWriterクラスは、どのようにしてインスタンス化するのでしょうか?
・注意する点(排他制御が必要)などありましたら、教えていただけないでしょうか。

質問ばかりで申し訳ありませんが、よろしくお願いいたします。

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

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

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

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

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

guest

回答4

0

いろんなやり方あるやろけど、僕ならTxtWriterを外から与える

//! この関数の中で(引数に与えた)writerを用いてテキストに書き込む void Process(const short* input, short* output, **TxtWriter* writer**);

あるいは void setWriter(TxtWriter writer) { m_pTxtWriter = writer; }* を追加。
※ ClassAのコンストラクタ引数に加えてもいい

[追記] ClassAにしてみればTxtWriterは処理のための道具であり、
そいつが唯一ひとつか/共有されてるかなんてどーでもいいこと。
だから std::shared_ptr<TxtWriter> をメンバに持つのは(個人的には)好まない。

※ 「TxtWriterはClassAの所有物だ」と考えるならその限りではないけどねー
要は設計者の意図次第かと。

投稿2021/08/31 01:57

編集2021/08/31 02:28
episteme

総合スコア16614

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

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

fana

2021/08/31 03:00 編集

> void setWriter(TxtWriter* writer) { m_pTxtWriter = writer; } とは,「writerの指す先へのアクセスの正当性(寿命管理)については,これを与える側で然るべく担保せよ」という話であり, (このことはいちいち注釈なりに書かずとも引数の型(とメソッドの名前)で十分に表明できている) で,そのことの具体的な手段として shared_ptr なりを用いる実装がしたいならばそれは ClassA の外側で好きにやればいい …と読みましたが妥当でしょうか? 師匠!
episteme

2021/08/31 03:28

御意。 「writerの生死についてはClassAの責任範囲ではない」って立場なら、 TxtWriter*(or TxtWriter&)を引き渡して"コレ使って処理しろ"って言えばいい。
fana

2021/08/31 03:45

ご教示いただきまするは ありがたきしあわせに存じまする
guest

0

基本的な方策としては、以下を思いつきます。
0. ClassAのコンストラクタで、利用するTxtWriterを受け取る。TxtWriterのインスタンスが共通かどうかはClassAの利用側でコントロールする。
0. ClassAのstaticなメンバ変数としてTxtWriterの実体または参照先(単純ポインタやstd::shared_ptr<TxtWriter>等)を保持する。

質問の内容だとよくわからない点として、ClassAが共用で利用するTxtWriterは、ファイルパスが固定だったりするんですかね?ってことですね。設定によってファイルパスが変わるとか、プロセスの動作中に途中でファイルを切り替えるとか、プロセスの動作中にファイルのオープン/クローズが必要になるとか、そういった要件次第で最適な設計はだいぶ変わると思います。

投稿2021/08/31 01:57

ttact

総合スコア170

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

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

0

ベストアンサー

TxtWriterの出力先が固定であるなら、シングルトンにすればよいのではないかと。
シングルトンのベターな実装方法


[補足の点について]

・TxtWriterクラスは、どのようにしてインスタンス化するのでしょうか?

シングルトンクラスにはインスタンス取得用のゲッターを用意します。
(リンク先の記事だとget_instance() がそれ)

・注意する点(排他制御が必要)などありましたら、教えていただけないでしょうか。

fwriteはスレッドセーフなので、fprintf_sも多分スレッドセーフだと思いますが、複数回に分けて呼び出してるなら、マルチスレッド環境下だと

  1. スレッド1のfprintf_s処理
  2. スレッド1のfprintf_s処理
  3. スレッド2のfprintf_s処理
  4. スレッド1のfprintf_s処理

という事が起こり得るので、WriteLineの呼出順で出力順序を保証したいなら、WriteLineの開始終了で同期オブジェクト(クリティカルセクションやmutex)で保護してやる必要があるでしょう。シングルスレッドでやるなら、別に気にしなくていいです。
Win32APIでスレッド間の排他制御(クリティカルセクション)を行う

最近のC++には疎いけど、もう標準ライブラリで用意されていたりするのかな?
C++11における同期処理(std::mutex, std::unique_guard, std::lock_guard, std::condition_variable)

投稿2021/08/31 01:53

編集2021/08/31 05:08
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

0

補足

に対して.

・TxtWriterクラスは、どのようにしてインスタンス化するのでしょうか?

ClassAのインスタンス生成時に,TxtWriterが無ければ作り,
ClassAのインスタンス破棄時に,自身が最後のインスタンスであればTxtWriterを捨てる

とかで良いのでは.

C++

1//雰囲気コード 2class ClassA 3{ 4public: 5 ClassA() //(※他のコンストラクタがあるなら忘れずに同じことをするように) 6 { 7 if( !ms_pTW ){ ms_pTW = new TxtWriter(); } 8 ++ms_nInstance; 9 } 10 11 ~ClassA() 12 { 13 if( --ms_nInstance == 0 ) 14 {//自身が最後のインスタンスだったなら捨てる 15 delete ms_pTW; 16 ms_pTW = nullptr; 17 } 18 } 19 20private: 21 class TxtWriter{ /*(略)*/ }; 22 23 static TxtWriter *ms_pTW; 24 static size_t ms_nInstance; 25}; 26 27ClassA::TxtWriter *ClassA::ms_pTW = nullptr; 28size_t ClassA::ms_nInstance = 0;

[追記]
スマートポインタで強引に.
うーん…

C++

1class ClassA 2{ 3private: 4 class TxtWriter{ /*(略)*/ }; 5 6public: 7 //ClassA のインスタンス生成手段 8 static ClassA Create() 9 { 10 auto spTW = ms_wpTW.lock(); 11 if( !spTW ) 12 { 13 spTW = std::make_shared<TxtWriter>(); 14 ms_wpTW = spTW; 15 } 16 return ClassA( spTW ); 17 } 18 19private: 20 //private ctor 21 ClassA( std::shared_ptr< TxtWriter > spTW ) : m_spTW(spTW) {} 22 //data 23 std::shared_ptr< TxtWriter > m_spTW; 24 //static 25 static std::weak_ptr<TxtWriter> ms_wpTW; 26}; 27 28std::weak_ptr<ClassA::TxtWriter> ClassA::ms_wpTW;

投稿2021/08/31 04:35

編集2021/08/31 04:53
fana

総合スコア11708

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

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

episteme

2021/08/31 04:46

TxtWriterがClassAの所有物であり、全インスタンスがひとつのTxtWriterを共有したいなら、 class変数(=static変数)となるでしょねー
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問