C ++のmain()内で何も宣言せずに、コンパイル後にアプリケーションが機能するようにするにはどうすればよいですか?


86

インタビューで、私は次のような質問に直面しました。

あなたの友人はあなたにフィボナッチ数をコンソールに印刷する単一のソースコードファイルを与えました。main()ブロックは空であり、その中にステートメントがないことに注意してください。

これがどのように可能であるかを説明してください(ヒント:グローバルインスタンス!)

私は本当にこれについて知りたいです、どうしてそのようなことが可能であるのか!


26
ヒントを見てください!
R. Martinho Fernandes

14
1)聞いたことがない、2)面接で尋ねられるので便利な雑学クイズ、3)知っておくべき言語の興味深い応用、4)認識して、顔の誰でも刺すことができる彼らが実際に本番コードでそれを使用しているのを見ると、さびたシブ。
OmnipotentEntity 2013

4
有能でプロのC ++プログラマーは、この質問に対する答えを知っています。この面接の質問の目的が、面接対象の人が有能でプロのC ++プログラマーであるかどうかを判断することである場合、質問は彼らに答えを与えるべきではありません。
ジョンダイブリング2013

1
インタビューの設定では、1つの代替方法は、コード内の任意の関数内にロジックを持ち、assertor#pragma messageなどを使用して出力をログに記録することです。これにより、コンパイル中に出力がコンソールにリダイレクトされます。プログラムが完全にコンパイルされることは決してないかもしれませんが、これは確かに面接中にあなたの「すぐに使える」考えを示すための楽しい方法です。これは、生成されるバイナリについては何も言及していないため、引用された質問を満たします。むしろ、コンソールに「もの」を表示できるCファイルについて話しているだけです。;-)
TheCodeArtist 2013

1
IOCCの面接でしたか?:-)わかりました、私は自分の工場を初期化するため、またはいくつかのテストコードを実行するためにそれを頻繁に行うことを認めます。ところで、「単一のソースコードファイル」もヒントであり、エントリパイント(デフォルトではメイン)がリンカーに置き換えられていないことを示しています。
Valentin Heinitz 2015年

回答:


127

ほとんどの場合、次のように実装されます(またはその変形):

 void print_fibs() 
 {
       //implementation
 }

 int ignore = (print_fibs(), 0);

 int main() {}

このコードでは、関数ignoreに入る前にグローバル変数を初期化する必要がありますmain()。ここで、グローバルを初期化print_fibs()するには、何でもできる場所で実行する必要があります。この場合、フィボナッチ数を計算して出力します。私が次の質問で示したのと同様のこと(私はずっと前に尋ねました):

このようなコードは安全ではないため、一般的には避けるのが最善であることに注意してください。たとえば、実行std::cout時にオブジェクトが初期化print_fibs()されない場合があります。初期化されている場合std::cout、関数はどうなりますか?ただし、他の状況でそのような初期化順序に依存しない場合は、初期化関数を呼び出しても安全です(これはCおよびC ++では一般的な方法です)。


3
@Nawaz正確な保証を引用する価値があるでしょう。翻訳ユニット内のオブジェクトは、順番に初期化されることが保証されています。標準ストリームオブジェクトは、std::ios_base::Initオブジェクトの最初の初期化の前または最中に初期化されることが保証されています。また、名前空間スコープにオブジェクトの<iostream>インスタンスが含まれているかのように動作することが保証さstd::ios_base_Initれています。
James Kanze 2013

3
@ Steve314:何も返さないので、式全体の型(print_fibs(), 0)int。であることを確認するためにコンマ演算子を使用しました。これがオンラインデモです。
nawaz 2013

1
@Nawaz void関数とコンマ演算子の代わりにbool、と変数を返すこともできますbool fibsPrinted。関数がここでのみ機能する場合は、おそらく少しクリーンです。(しかし、違いはおそらく心配するのに十分ではありません。)
James Kanze 2013

1
+1、素晴らしい話をしましょう。この質問とこの回答に賛成するためだけにstackoverflowに参加する必要がありました。
固定小数点

1
@Nawazあなたの主張が何なのかわかりません。の定義はstd::coutライブラリのどこかにあります。しかし、すでに指摘したように、標準でstd::ios_base::Initオブジェクトの最初のコンストラクターが終了する前に初期化する必要があり<iostream>std::ios_base::Initオブジェクトが名前空間スコープで定義されているかのようにインクルードが動作する必要があります。<iostream>初期化されるオブジェクトの定義の前に変換ユニットが含まれている場合、std::cout構築されることが保証されます。
James Kanze 2013

18

お役に立てれば

class cls
{
  public:
    cls()
    {
      // Your code for fibonacci series
    }
} objCls;

int main()
{
}

したがって、クラスのグローバル変数が宣言されるとすぐにコンストラクターが呼び出され、そこにフィボナッチ数列を出力するロジックが追加されます。


9

はい、可能です。オブジェクトコンストラクターでフィボナッチ数を計算するオブジェクトのグローバルインスタンスを宣言する必要があります。


6
イニシャライザがフィボナッチ数を計算するオブジェクトのグローバルインスタンスを宣言する必要があります。
James Kanze 2013

4

私はあなたが言うようないくつかの例を知っています。それを取得する1つの方法は、テンプレートメタプログラミングを使用することです。これを使用すると、計算プロセスをコンパイルに移すことができます。

ここでは、フィボナッチ数の例を取得できます

静的クラスコンストラクターで使用し、main関数にコードを記述しなくても数値を記述できる場合。

それがあなたを助けることを願っています。


3

グローバル/静的変数の初期化中に問題が発生する可能性があります。コードは、アプリケーションの起動時にトリガーされます。


3

ファイルスコープオブジェクトのすべての[*]コンストラクターはmain、非オブジェクトファイルスコープ変数のすべての初期化式と同様に、に到達する前に呼び出されます。

編集:また、すべてのファイルスコープオブジェクトのall [*]デストラクタは、main終了後に構築の逆の順序で呼び出されます。理論的には、フィボナッチプログラムをオブジェクトのデストラクタに入れることができます。

[*]「all」は、プログラムが直接リンクされていないライブラリを動的にロードおよびアンロードする動作を無視することに注意してください。ただし、これらは技術的には基本C ++言語の範囲外です。


すべて?後に明示的にロードされるdll内のものでさえmain
James Kanze 2013

ええと、C ++は動的にロードされるライブラリを技術的に定義していないので、純粋なC ++内では、私のステートメントは正しいです。したがって、「すべて、メインに到達した後にロードされたDLL / DSOに含まれるイニシャライザーとファイルスコープオブジェクトを除いて」とシェーディングします。この場合、mainは空であるため、これらのDLL / DSOはデストラクタによってロードされる必要がありますが、これはひねくれたものです。しかし、これはコンピュータサイエンスなので、「すべて」のような言葉には注意が必要だと思います。
ジョーZ

上記の回答に「すべて」に関する警告を追加し、dtorに関するメモも追加しました。
ジョーZ

はい、しかしうまくいけばそれが来るでしょう。C ++ 11より前のバージョンには、DLLを許可するためのイタチの文言が含まれていましたが、実際には、実際のすべての実装に保証があり、多くのコードがそれに依存していても、技術的には保証が常に存在するとは限りませんでした。C ++ 11は、少なくともそれを修正しました。
James Kanze 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.