2010-11-21 23 views
27

グローバルオブジェクトを作成するときにコンストラクタ内のすべてのアクションを開始できます。では、C++でmain()関数が本当に必要なのですか?C++でmain()が本当に必要ですか?

私は、そうすることが悪い習慣と考えられることが理解できます。私は好奇心を求めています。

+3

興味深い質問です。私は単一のグローバルオブジェクトを持つことは決して考えなかった。あなたが言うように、悪い習慣が、それにもかかわらず興味深いです。 –

+0

このようなものは、 'CWinApp'の単一インスタンスを持つMFCで実装されています – Andrey

+3

@CwinAppでは、MFCはmain/winmainを提供します。したがって、MFCにはmain()関数があります。 –

答えて

30

ホストされたC++実装でプログラムを実行する場合は、main関数が必要です。それは物事がどのように定義されているかです。あなたはもちろんそれを空にしておくことができます。技術的な面では、リンカは実行時ライブラリで使用されているmainシンボルを解決しようとしています(これを省略する特別な意図はありません。標準でmainがオプションであると指定された場合、もちろん実装は解決策を生む可能性がありますが、それはパラレルユニバースで発生する必要があります。

"グローバルオブジェクトのコンストラクタで実行が開始される"場合は、異なる翻訳単位で定義された名前空間スコープオブジェクトの構築順序に関する多くの問題に気を付けることに注意してください(だからエントリポイント?答えは:複数のエントリポイントがあり、最初にどのエントリポイントが実行されるのかは不明です!)。 C++ 03では、coutが正しく構築されていることさえ保証されていません(C++ 0xでは、先行するインクルードが<iostream>である限り、コードが使用しようとする前に保証されています)。

::mainでの実行を適切に開始すると、これらの問題は発生せず、回避する必要もありません(非常に扱いにくい)。コメントで述べたように


は彼がmain内でインスタンス化されるクラスの名前を教えてくれたことで、ユーザからmainを隠すしかし、いくつかのシステムがあります。これは、次の例

class MyApp { 
public: 
    MyApp(std::vector<std::string> const& argv); 

    int run() { 
     /* code comes here */ 
     return 0; 
    }; 
}; 

IMPLEMENT_APP(MyApp); 

に、このシステムのユーザに似ています、完全にmain機能があることが隠されていますが、

#define IMPLEMENT_APP(AppClass) \ 
    int main(int argc, char **argv) { \ 
    AppClass m(std::vector<std::string>(argv, argv + argc)); \ 
    return m.run(); \ 
    } 

を次のようにそのマクロは、実際には、このような主な機能を定義しますこれは、上述の不特定の構成の順序の問題を持たない。それらの利点は、異なるレベルのより高いレベルのエントリポイントで動作することです。たとえば、Windows GUIプログラムはWinMain関数で起動します。IMPLEMENT_APPは、そのプラットフォームではなくそのような関数を定義できます。

+1

ホストされているものは何ですか? –

+0

@bjarkefこれは素敵な新しいSOの質問を構成する必要があります(それの本当の重複を認識していません)。しかし、要するに、ファイルや例外などのOSサポートがない実装です。言い換えれば、最小限のものです。独立した実装を対象とするために、OSカーネルを記述することができます。 –

+0

私はC++が本当にPythonのように 'SystemExit'例外を使うことができると思います。それは 'exit'と呼ぶよりもはるかにクリーンです。しかし、当然のことながら、どのスレッドが実際に重要な例外をスローするかという問題があります。 – Omnifarious

0

グローバルオブジェクトが不可能な実装や、(モバイルや埋め込み領域などの)特にそのようなオブジェクトでは不可能なコンストラクタが存在しない実装があります。

+1

良い点ですが、彼はもっと「普通の」C++実装について話していると思います。 –

2

グローバルオブジェクトが複数作成されている場合、最初に実行するコンストラクタは保証されません。

3

一般に、アプリケーションはエントリポイントを必要とし、mainはそのエントリポイントです。 mainの前にグローバルの初期化が起こるかもしれないという事実はほとんど無関係です。コンソールやGUIアプリケーションを書く場合は、mainをリンクする必要があります。奇妙な意図しない目的のために他の機能を使用するのではなく、そのルーチンがアプリケーションの主な実行を担うことが唯一の良い方法です。

+0

私は同意する、オブジェクトの初期化は2つのポイントでのみ発生します。ロード時に、EXEまたはDLLをメモリに引き込み、エントリポイント(メイン)が設定されているスタック初期化時に実行します。 – Eric

2

静的または動的ライブラリコードを作成する場合は、自分でmainを定義する必要はありませんが、それでもそれを持つプログラムでは実行されます。

3

さて、C++標準の観点からは、依然として必要です。しかし、私はあなたの質問がそれとは違うと思う。

あなたが考えているやり方は、あまりにも多くの問題を引き起こすと思います。

例えば、多くの環境では、プログラム全体を実行した結果として、mainの戻り値が与えられます。そして、それはコンストラクタから複製するのは本当に難しいでしょう。コードのいくつかのビットは、もちろんexitを呼び出すことができますが、これはgotoを使用しているようで、スタック上の何かの破壊をスキップします。あなたは0以外の終了コードを生成するために代わりにスローした特別な例外を持つことで問題を解決しようとする可能性があります。

しかし、まだグローバルコンストラクタの実行順序が定義されていないという問題があります。つまり、グローバルオブジェクトの特定のコンストラクタでは、他のグローバルオブジェクトがまだ存在しているかどうかを前提にすることはできません。

コンストラクタの順序問題は、各コンストラクタが独自のスレッドを取得するということだけで解決できます。他のグローバルオブジェクトにアクセスするには、条件変数が構築されるまで待たなければなりません。それはデッドロックを求めているだけで、デッドロックは本当にデバッグが難しいでしょう。特別な「プログラムからの戻り値」例外で終了するスレッドが、プログラム全体の実際の戻り値を構成するという問題もあります。

mainを取り除きたい場合、2つの問題はキラーだと思います。

そして、基本的にはmainに相当するものはないとは思えません。例えば、Javaでは、静的関数が呼び出されるmainという外部から提供されるクラス名があります。 Pythonでは、__main__モジュールがあります。 perlには、コマンドラインで指定したスクリプトがあります。

4

はい!あなたはメインを離れてしまうことができます。

免責事項:可能であれば、実行する必要があるかどうかを尋ねました。これは完全にサポートされていない、悪い考えです。私はこれを自分でやってきました。なぜなら、私が入ってこない理由のためですが、私はそれを推薦していません。私の目的はメインを取り除くことではないが、それも同様に行うことができる。次のように

基本的な手順は以下のとおりです。

  1. コンパイラのCRTのソースディレクトリにcrt0.cを検索します。
  2. crt0.cをプロジェクトに追加します(オリジナルではなくコピー)。
  3. crt0.cからメインへの呼び出しを検索して削除します。

コンパイルおよびリンクするのは難しい場合があります。どれくらい難しいかは、コンパイラとコンパイラのバージョンによって異なります。

を追加しました

私はただのVisual Studio 2008でそれをやったので、ここであなたはそれがそのコンパイラで動作するように取得するために取らなければならない正確な手順です。

  1. 新しいC++ Win32コンソールアプリケーションを作成します(次へをクリックしてEmpty Projectをチェックしてください)。
  2. 新しい項目を追加.. C++ファイルですが、名前はcrt0.c(.cppではありません)です。
  3. 内容をC:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src\crt0.cにコピーし、crt0.cに貼り付けます。
  4. mainret = _tmain(__argc, _targv, _tenviron);を見つけてコメントしてください。
  5. crt0.cを右クリックし、[プロパティ]を選択します。
  6. C/C++ - >一般 - >追加インクルードディレクトリ= "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src"を設定します。
  7. C/C++ - >プリプロセッサ - >プリプロセッサ定義= _CRTBLDを設定します。
  8. [OK]をクリックします。
  9. プロジェクト名を右クリックし、[プロパティ]を選択します。
  10. C/C++ - >コード生成 - >ランタイムライブラリ= Multi-threaded Debug (/MTd)(*)を設定します。
  11. [OK]をクリックします。
  12. 新しい項目を追加してください。C++ File、名前は何でもいいです(この例ではapp.cpp)。
  13. app.cppに以下のコードを貼り付けて実行してください。

(*)ランタイムDLLを使用することはできません。ランタイムライブラリに静的にリンクする必要があります。

#include <iostream> 

class App 
{ 
    public: App() 
    { 
     std::cout << "Hello, World! I have no main!" << std::endl; 
    } 
}; 

static App theApp; 

を追加しました

私たちはすべての主要な削除の結果を理解することだと思うように私は余分に終了コールと寿命についての宣伝文を削除しました。

ウルトラネクロ

私はこの答えに出くわし、それ以下のジョンDiblingの異議の両方をお読みください。私が上記の手順が何をしているのか、なぜそれが本当にプログラムからメインを完全に削除するのかを説明していないことは明らかでした。

ジョンは、CRTには「メインが常にあります」と主張しています。これらの言葉は厳密には正しいものではありませんが、声明の精神はそうです。 MainはCRTが提供する機能ではないため、あなた自身で追加する必要があります。その関数への呼び出しは、CRTで提供されるエントリポイント関数内にあります。

すべてのC/C++プログラムのエントリポイントは、「crt0」という名前のモジュール内の関数です。私はこれが規約であるのか言語仕様の一部であるのかは分かりませんが、私が出会ったすべてのC/C++コンパイラ(それはたくさんあります)がそれを使用しています。この機能は、基本的に3つの事柄ん:

  • メインCRT
  • コールは、上記の例では、コールが_tmainそれが可能にするために、いくつかのマクロの魔法です
    1. 取り壊す初期化「メイン」が持つことができる様々な形式があり、そのうちいくつかはこの場合はVS固有です。

      CRTから 'crt0'モジュールを削除し、新しいモジュールに置き換えます。これは、ランタイムDLLを使用できない理由です。追加しているものと同じエントリポイント名を持つDLL内にすでに関数があります(2)。静的にリンクすると、CRTは.libファイルのコレクションであり、リンカーによって.libモジュール全体をオーバーライドすることができます。この場合、1つの機能しか持たないモジュールです。

      新しいプログラムには、CRT0モジュールを除いた在庫CRTが含まれていますが、独自のCRT0モジュールが作成されています。ここでmainへの呼び出しを取り除きます。だからどこにもメインはない!

      (2)crt0.cファイルのエントリポイント関数の名前を変更し、リンカ設定のエントリポイントを変更することで、ランタイムDLLを使用できると思われるかもしれません。ただし、コンパイラはエントリポイントの変更を認識しておらず、DLLには提供していない「メイン」関数への外部参照が含まれているため、コンパイルされません。

    +0

    私は本当にこれが好きです! – vy32

    +0

    これは本当にmainを取り除かない。メインはいつもここでCRTに住んでいた。 '_tmain'はCRTの' main'が呼び出したものです。今はそれはしません。 –

    +0

    @ジョン:ハァッか。これにより、元のクエリの主題であるメインメソッドを提供する必要がなくなります。これは、不明瞭な場所にメインを隠すことではなく、「問題」の原因となっているメインへのCRT呼び出し(メインを定義しないことで通常は得られるリンカエラー)を取り除くことによって行われます。 – Tergiver

    2

    ウィンドウをコーディングする場合、ではなくを実行します。

    グローバルオブジェクトのコンストラクタ内から完全にアプリケーションを実行すると、ちょっとはうまくいくかもしれませんが、遅かれ早かれ不正な関数を呼び出し、警告なしで終了するプログラムになります。

    1. グローバルオブジェクトコンストラクタは、Cランタイムの起動時に実行されます。
    2. CランタイムDLLのDLLMain中にCランタイムスタートアップコードが実行されます
    3. DLLMainの間、DLLローダーロックが保持されています。
    4. 既にDLLローダーロックを保持している間に別のDLLを読み込むと、処理が急激に終了します。

    あなたのアプリケーション全体を1つの実行ファイルにコンパイルしても、あなたを救うことはできません。多くのWin32呼び出しは、静かにシステムDLLを読み込む可能性があります。

    関連する問題