グローバルオブジェクトを作成するときにコンストラクタ内のすべてのアクションを開始できます。では、C++でmain()関数が本当に必要なのですか?C++でmain()が本当に必要ですか?
私は、そうすることが悪い習慣と考えられることが理解できます。私は好奇心を求めています。
グローバルオブジェクトを作成するときにコンストラクタ内のすべてのアクションを開始できます。では、C++でmain()関数が本当に必要なのですか?C++でmain()が本当に必要ですか?
私は、そうすることが悪い習慣と考えられることが理解できます。私は好奇心を求めています。
ホストされた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
は、そのプラットフォームではなくそのような関数を定義できます。
ホストされているものは何ですか? –
@bjarkefこれは素敵な新しいSOの質問を構成する必要があります(それの本当の重複を認識していません)。しかし、要するに、ファイルや例外などのOSサポートがない実装です。言い換えれば、最小限のものです。独立した実装を対象とするために、OSカーネルを記述することができます。 –
私はC++が本当にPythonのように 'SystemExit'例外を使うことができると思います。それは 'exit'と呼ぶよりもはるかにクリーンです。しかし、当然のことながら、どのスレッドが実際に重要な例外をスローするかという問題があります。 – Omnifarious
グローバルオブジェクトが不可能な実装や、(モバイルや埋め込み領域などの)特にそのようなオブジェクトでは不可能なコンストラクタが存在しない実装があります。
良い点ですが、彼はもっと「普通の」C++実装について話していると思います。 –
グローバルオブジェクトが複数作成されている場合、最初に実行するコンストラクタは保証されません。
一般に、アプリケーションはエントリポイントを必要とし、main
はそのエントリポイントです。 main
の前にグローバルの初期化が起こるかもしれないという事実はほとんど無関係です。コンソールやGUIアプリケーションを書く場合は、main
をリンクする必要があります。奇妙な意図しない目的のために他の機能を使用するのではなく、そのルーチンがアプリケーションの主な実行を担うことが唯一の良い方法です。
私は同意する、オブジェクトの初期化は2つのポイントでのみ発生します。ロード時に、EXEまたはDLLをメモリに引き込み、エントリポイント(メイン)が設定されているスタック初期化時に実行します。 – Eric
静的または動的ライブラリコードを作成する場合は、自分でmain
を定義する必要はありませんが、それでもそれを持つプログラムでは実行されます。
さて、C++標準の観点からは、依然として必要です。しかし、私はあなたの質問がそれとは違うと思う。
あなたが考えているやり方は、あまりにも多くの問題を引き起こすと思います。
例えば、多くの環境では、プログラム全体を実行した結果として、main
の戻り値が与えられます。そして、それはコンストラクタから複製するのは本当に難しいでしょう。コードのいくつかのビットは、もちろんexit
を呼び出すことができますが、これはgoto
を使用しているようで、スタック上の何かの破壊をスキップします。あなたは0
以外の終了コードを生成するために代わりにスローした特別な例外を持つことで問題を解決しようとする可能性があります。
しかし、まだグローバルコンストラクタの実行順序が定義されていないという問題があります。つまり、グローバルオブジェクトの特定のコンストラクタでは、他のグローバルオブジェクトがまだ存在しているかどうかを前提にすることはできません。
コンストラクタの順序問題は、各コンストラクタが独自のスレッドを取得するということだけで解決できます。他のグローバルオブジェクトにアクセスするには、条件変数が構築されるまで待たなければなりません。それはデッドロックを求めているだけで、デッドロックは本当にデバッグが難しいでしょう。特別な「プログラムからの戻り値」例外で終了するスレッドが、プログラム全体の実際の戻り値を構成するという問題もあります。
main
を取り除きたい場合、2つの問題はキラーだと思います。
そして、基本的にはmain
に相当するものはないとは思えません。例えば、Javaでは、静的関数が呼び出されるmain
という外部から提供されるクラス名があります。 Pythonでは、__main__
モジュールがあります。 perl
には、コマンドラインで指定したスクリプトがあります。
はい!あなたはメインを離れてしまうことができます。
免責事項:可能であれば、実行する必要があるかどうかを尋ねました。これは完全にサポートされていない、悪い考えです。私はこれを自分でやってきました。なぜなら、私が入ってこない理由のためですが、私はそれを推薦していません。私の目的はメインを取り除くことではないが、それも同様に行うことができる。次のように
基本的な手順は以下のとおりです。
crt0.c
を検索します。crt0.c
をプロジェクトに追加します(オリジナルではなくコピー)。crt0.c
からメインへの呼び出しを検索して削除します。コンパイルおよびリンクするのは難しい場合があります。どれくらい難しいかは、コンパイラとコンパイラのバージョンによって異なります。
を追加しました
私はただのVisual Studio 2008でそれをやったので、ここであなたはそれがそのコンパイラで動作するように取得するために取らなければならない正確な手順です。
Empty Project
をチェックしてください)。crt0.c
(.cppではありません)です。C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src\crt0.c
にコピーし、crt0.c
に貼り付けます。mainret = _tmain(__argc, _targv, _tenviron);
を見つけてコメントしてください。crt0.c
を右クリックし、[プロパティ]を選択します。"C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src"
を設定します。_CRTBLD
を設定します。Multi-threaded Debug (/MTd)
(*)を設定します。app.cpp
)。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から 'crt0'モジュールを削除し、新しいモジュールに置き換えます。これは、ランタイムDLLを使用できない理由です。追加しているものと同じエントリポイント名を持つDLL内にすでに関数があります(2)。静的にリンクすると、CRTは.libファイルのコレクションであり、リンカーによって.libモジュール全体をオーバーライドすることができます。この場合、1つの機能しか持たないモジュールです。
新しいプログラムには、CRT0モジュールを除いた在庫CRTが含まれていますが、独自のCRT0モジュールが作成されています。ここでmainへの呼び出しを取り除きます。だからどこにもメインはない!
(2)crt0.cファイルのエントリポイント関数の名前を変更し、リンカ設定のエントリポイントを変更することで、ランタイムDLLを使用できると思われるかもしれません。ただし、コンパイラはエントリポイントの変更を認識しておらず、DLLには提供していない「メイン」関数への外部参照が含まれているため、コンパイルされません。
ウィンドウをコーディングする場合、ではなくを実行します。
グローバルオブジェクトのコンストラクタ内から完全にアプリケーションを実行すると、ちょっとはうまくいくかもしれませんが、遅かれ早かれ不正な関数を呼び出し、警告なしで終了するプログラムになります。
あなたのアプリケーション全体を1つの実行ファイルにコンパイルしても、あなたを救うことはできません。多くのWin32呼び出しは、静かにシステムDLLを読み込む可能性があります。
興味深い質問です。私は単一のグローバルオブジェクトを持つことは決して考えなかった。あなたが言うように、悪い習慣が、それにもかかわらず興味深いです。 –
このようなものは、 'CWinApp'の単一インスタンスを持つMFCで実装されています – Andrey
@CwinAppでは、MFCはmain/winmainを提供します。したがって、MFCにはmain()関数があります。 –