🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C++

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

Q&A

解決済

3回答

2735閲覧

C++ - 静的関数をプログラム起動時に呼び出す方法

tiitoi

総合スコア21956

C++

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

0グッド

0クリップ

投稿2020/11/27 11:28

編集2020/11/27 11:47

質問内容

静的メンバ変数 (table1, table2) を初期化するための静的メンバ関数 initialize() があります。
今は「静的メンバ変数にアクセスする前に必ず initialize() を一度呼ぶこと」としているのですが、使う側が呼び出すのを忘れて、静的メンバ変数にアクセスした場合、エラーになってしまいます。

そのようなことがないように、プログラム起動時 (main() より前) に自動に initialize() が呼び出されて、静的メンバ変数を初期化されるようにしたいのですが、そのような方法はありますでしょうか。

よろしくおねがいします。

環境

  • C++17

コード

※ 以下のコードでは、メンバ変数の中身がコンパイル時に決定できますが、実際はファイルを読み込んで初期化する処理のため、コンパイル時に中身は決定できません。

cpp

1#include <algorithm> 2#include <iostream> 3#include <vector> 4 5class MyClass 6{ 7public: 8 // プログラム起動時に呼び出してほしい関数 9 static void initliaze() 10 { 11 if (!table1.empty()) 12 return; // 初期化済み 13 14 make_table(table1); 15 make_table(table2); 16 } 17 18 static void make_table(std::vector<size_t> &table) 19 { 20 table.resize(10); 21 for (size_t i = 0; i < 10; i++) 22 table[i] = i; 23 } 24 25 static std::vector<size_t> table1; 26 static std::vector<size_t> table2; 27}; 28 29std::vector<size_t> MyClass::table1; 30std::vector<size_t> MyClass::table2; 31 32int main(int, char **) 33{ 34 MyClass::initliaze(); 35 std::cout << MyClass::table1[3] << std::endl; 36} 37

解決方法

教えていただいた内容をもとに以下のように修正しました。
ご回答ありがとうございました。

cpp

1#include <algorithm> 2#include <iostream> 3#include <vector> 4 5class MyClass 6{ 7public: 8 MyClass() 9 { 10 initliaze(); 11 } 12 13 // プログラム起動時に呼び出してほしい関数 14 static void initliaze() 15 { 16 if (!table1.empty()) 17 return; // 初期化済み 18 19 make_table(table1); 20 make_table(table2); 21 } 22 23 static void make_table(std::vector<size_t> &table) 24 { 25 table.resize(10); 26 for (size_t i = 0; i < 10; i++) 27 table[i] = i; 28 } 29 30 static std::vector<size_t> table1; 31 static std::vector<size_t> table2; 32}; 33 34std::vector<size_t> MyClass::table1; 35std::vector<size_t> MyClass::table2; 36 37static MyClass inst; 38 39int main(int, char **) 40{ 41 std::cout << MyClass::table1[3] << std::endl; 42}

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

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

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

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

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

guest

回答3

0

タイトルの内容に対しての回答とはなりませんが…


今は「静的メンバ変数にアクセスする前に必ず initialize() を一度呼ぶこと」としているのですが、使う側が呼び出すのを忘れて、静的メンバ変数にアクセスした場合、エラーになってしまいます。

といった話への対策として,「勝手にファイル読み込みを伴う初期化が実施される」という形はどうなんだろう? と思います.

例えば,

int main() { ... if( XXX ){ MyClassをつかう } else{ MyClassを使わない } }

みたいなコードでelse側のパスを通る際には,main()よりも前に行われるその処理は完全に不当なコストにもなりえるように思えます.
(それでさらにファイル読み込みがこけた場合は一体どうなるのか?という話も若干気になりますし)

何というか,ちょっと行き過ぎた施策であるような?


…とかなんとかいう感想を持ったので,initialize()を呼ぶことはユーザに委ねるままとして,「initialize()を呼ばないと使えない」ような形にする方向性として以下のような話を考えました.

  • 「静的メンバ変数にアクセス」を「静的メンバ関数にアクセス」に置き換える.
  • 静的メンバ関数が,ある型Xの参照を引数に取るならば,呼ぶ側はそのX型のオブジェクト(への参照)を手に入れない限りは,その静的メンバ関数を呼べない.
  • で,X型オブジェクト(への参照)を手に入れる手段がinitialize()であればよいのでは?

ざっくりと,こんなような…?

C++

1//X型.何もかもMyClassからしか触れない 2class X 3{ 4 friend class MyClass; 5private: 6 X(){} 7 X( const X& ) = delete; 8 X &operator=( const X& ) = delete; 9 10 bool empty() const { return table1.empty(); } 11 12 // 13 std::vector<size_t> table1; 14}; 15 16class MyClass 17{ 18private: 19 static X ms_X; 20public: 21 // プログラム起動時に呼び出してほしい関数 22 static X& initliaze() 23 { 24 if( ms_X.empty() ) 25 { 26 //ms_Xの初期化.table1をファイル内容から作る? 27 } 28 return ms_X; 29 } 30 31 //静的メンバ関数.引数が必要 32 static std::vector<size_t> &table1( X &x ){ return x.table1; } 33}; 34 35X MyClass::ms_X;

投稿2020/11/27 14:37

編集2020/11/27 14:38
fana

総合スコア11985

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

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

HogeAnimalLover

2020/11/27 15:10

『「静的メンバ変数にアクセス」を「静的メンバ関数にアクセス」に置き換える.』ことを許容するならば、初期化の未/済のフラグを用意の上、この判定を静的メンバ関数内で行えばいいと思います。これもコストが大きいでしょうか?
fana

2020/11/27 15:46 編集

判定コストは小さいでしょうが, フラグを見て「未初期化」状態だった場合にはどうするのでしょうか? その場でエラーとするならば,"initialize()を忘れるとエラーになる" という問題が解決しませんし, 「未初期化だったらその場で自動的にinitilaize()を走らせる」とかだと,initialize()処理が重い場合にはそのことが利用側にとって問題になるかもしれません.(「何故か初回だけ異様に重い」みたいな謎の挙動に悩まされることになるかも?)
fana

2020/11/27 15:54

とにかく, ファイル読み込みによる初期化なる(重い可能性があって,失敗する可能性も十分にありそうな)ものが, 本当に行う必要があるのか否かに関わらず常に謎のタイミングで勝手に実施される という話がなんだか嫌な感じだなぁ,的な? 「最初にinitialize()を1回だけ呼んでね」的な話になっている物って,わりと良く見るように思うし, なんというか,やろうとしていること自体が,お節介気味ではなかろうか?
HogeAnimalLover

2020/11/28 03:54

さすがにそこまでくると、具体的な状況次第です。あらゆる状況への対応は無理です、デフォルトの作りに近づくだけです。
tiitoi

2020/11/30 06:18

シングルトンパターンで、データを取得する際に初期化されていなければそのタイミングで初期化するというのもありですね。 アイデアを出していただきありがとうございます。
fana

2020/11/30 06:37

背景事情がわからないので何とも言えませんが, 「Initialize()を呼ばなかったからエラー」という話は,普通にそのままであってもよいのではないかな,と. ユーザ側からしたら,エラー原因さえ明確ならば,そこでコードを1回修正すればもうそれで終わりな話だろうと思いますし.
guest

0

ベストアンサー

静的インスタンスのコンストラクタならば、メイン関数よりも前に処理できると思います。動作未確認。

投稿2020/11/27 11:35

HogeAnimalLover

総合スコア4830

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

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

tiitoi

2020/11/27 11:45

コンストラクタから初期化関数を呼び出して静的インスタンスを作成することで解決できました。回答いただきありがとうございます。
HogeAnimalLover

2020/11/27 12:36

書き忘れましたが、これはコンピュータウイルスのエントリーポイント偽装にも使われたりします。「これだけでウイルス検知された」というのは存じませんが、念の為、お伝えしておきます。
tiitoi

2020/11/27 12:39

たしかに悪用しようと思ったら main() より前に任意のコードが実行できますね。補足ありがとうございます。
guest

0

public staticな変数の初期化であれば、初期化用のクラスを別に作って、そのコンストラクタで初期化をかける、というような方法はどうでしょうか(Qiita)?

投稿2020/11/27 11:35

maisumakun

総合スコア145967

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

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

tiitoi

2020/11/27 11:44 編集

初期化処理を行うコンストラクタを用意したクラスを用意して、そのインスタンスを作成することで初期化する方法は思いつきませんでした。回答いただきありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問