代码之家  ›  专栏  ›  技术社区  ›  onemasse

你真的需要C++中的一个主()吗?

c++
  •  27
  • onemasse  · 技术社区  · 15 年前

    据我所知,在创建全局对象时,可以启动构造函数中的所有操作。那么,你真的需要C++中的主函数吗?还是仅仅是遗留的?

    我能理解这样做可能被认为是不好的做法。我只是好奇地问。

    8 回复  |  直到 15 年前
        1
  •  30
  •   Johannes Schaub - litb    15 年前

    如果您想在托管C++实现中运行您的程序,则需要 main 功能。事情就是这样定义的。当然,如果你想的话,可以把它空着。在技术方面,链接器希望解决 主要的 运行时库中使用的符号(运行时库不知道您要忽略它的特殊意图—它仍然会发出对它的调用)。如果标准规定 主要的 是可选的,那么实现当然可以提出解决方案,但是这需要在一个并行的世界中发生。

    如果您使用“executionstartsintheconstructorofmyglobal object”,请注意,您会遇到许多与在不同转换单元中定义的命名空间作用域对象的构造顺序相关的问题(所以 入口点?答案是:您将有多个入口点,而首先执行的入口点是未指定的!)在C++ 03中,你甚至不能保证 cout 在C++0x中,只要有任何代码试图使用它,就可以保证它是正确的,只要有一个预先包含的 <iostream> ).

    如果正确地开始执行 ::main .


    正如评论中提到的那样 但是有几个系统隐藏 主要的 通过让用户告诉在 主要的 . 其工作原理类似于以下示例

    class MyApp {
    public:
      MyApp(std::vector<std::string> const& argv);
    
      int run() {
          /* code comes here */
          return 0;
      };
    };
    
    IMPLEMENT_APP(MyApp);
    

    对于这个系统的用户来说,完全隐藏了 主要的 函数,但该宏实际上会定义这样一个主函数,如下所示

    #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 然后可以在该平台上定义这样的函数。

        2
  •  4
  •   Tergiver    9 年前

    对! 你可以不用main。

    免责声明:你问是否可能,而不是是否应该这样做。这是一个完全不受支持的坏主意。我自己也这么做了,原因是我不想谈,但我不建议这样做。我的目的不是摆脱main,但它也可以做到这一点。

    基本步骤如下:

    1. 查找 crt0.c 在编译器的CRT源目录中。
    2. 添加 crt0.c公司 到您的项目(副本,而不是原件)。
    3. 查找并删除对main的调用 crt0.c公司 .

    让它编译和链接可能很困难;困难程度取决于哪个编译器和哪个编译器版本。

    补充

    我刚刚在Visual Studio 2008中完成了这项工作,下面是让它与编译器一起工作所需的具体步骤。

    1. 创建一个新的C++ Win32控制台应用程序(单击Next)并检查 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++-GT;通用& gt;附加目录 "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src" .
    7. 设置C/C++-GT;预处理器-gt;预处理器定义= _CRTBLD .
    8. 单击“确定”。
    9. 在项目名称上单击鼠标右键,然后选择“属性”。
    10. 设置C/C++ +Gt;代码生成-gt;运行时库= Multi-threaded Debug (/MTd) (*).
    11. 单击“确定”。
    12. 添加新项。。C++文件,命名它什么 app.cpp 在本例中)。
    13. 将下面的代码粘贴到 附录cpp 运行它。

    (*)不能使用运行时DLL,必须静态链接到运行时库。

    #include <iostream>
    
    class App
    {
        public: App()
        {
            std::cout << "Hello, World! I have no main!" << std::endl;
        }
    };
    
    static App theApp;
    

    补充

    我移除了超常的退出呼叫和关于生命的模糊,因为我认为我们都能理解移除主的后果。

    超坏死

    我刚刚找到这个答案,并阅读了它和约翰迪布林的反对意见如下。很明显,我没有解释上面的过程做什么,以及为什么它确实从程序中完全删除main。

    约翰断言在阴极射线管中“总有一个主管”。这些话并不完全正确,但声明的精神是正确的。Main不是CRT提供的函数,您必须自己添加它。对该函数的调用在CRT提供的入口点函数中。

    每个C/C++程序的入口点是一个名为CRT0的模块中的一个函数。我不确定这是一个约定还是语言规范的一部分,但是我遇到的每一个C/C++编译器都使用它。这个函数主要做三件事:

    1. 初始化CRT
    2. 主叫
    3. 拆毁

    在上面的例子中,调用是_tmain,但这是一些宏魔术,允许“main”可以有各种形式,其中一些在本例中是VS-specific。

    上面的过程是从CRT中删除模块crt0,并用一个新的模块替换它。这就是为什么不能使用运行时DLL,该DLL中已经有一个函数的入口点名称与我们要添加的相同(2)。静态链接时,CRT是.lib文件的集合,链接器允许您完全重写.lib模块。在这种情况下,只有一个功能的模块。

    我们的新程序包含股票CRT,减去它的CRT0模块,但有一个我们自己创建的CRT0模块。在那里我们取消了对main的呼叫。所以哪里都没有主食!

    (2)您可能认为可以通过重命名crt0.c文件中的入口点函数并在链接器设置中更改入口点来使用运行时DLL。但是,编译器不知道入口点的更改,并且DLL包含对您没有提供的“main”函数的外部引用,因此它不会编译。

        3
  •  3
  •   hobbs    15 年前

    一般来说,应用程序需要一个入口点,并且 main 就是那个入口点。全局变量的初始化可能发生在 主要的 几乎是无关紧要的。如果你正在编写一个控制台或GUI应用程序,你必须有一个 主要的 为了让它链接起来,最好让这个例程负责应用程序的主要执行,而不是将其他功能用于奇怪的意外目的。

        4
  •  3
  •   Omnifarious    15 年前

    从C++标准的角度来看,是的,它仍然是必需的。但我怀疑你的问题与此不同。

    我认为你想的那样做会引起太多的问题。

    例如,在许多环境中 main 作为运行整个程序的状态结果给出。从构造器中进行复制是非常困难的。一些代码仍然可以调用 exit 当然,但这看起来像是在用 goto 不会破坏任何东西。您可以尝试通过抛出一个特殊的异常来解决问题,而不是生成一个退出代码。 0 .

    但是,您仍然会遇到未定义全局构造函数的执行顺序的问题。这意味着在全局对象的任何特定构造函数中,您将无法对是否存在任何其他全局对象做出任何假设。

    你可以试着解决构造器顺序问题,只要说每个构造器都有自己的线程,如果你想访问任何其他的全局对象,你就必须等待一个条件变量,直到它们说它们已经构造好。不过,这只是要求出现死锁,而这些死锁将很难调试。您还可以知道哪个线程退出程序的“返回值”将构成整个程序的实际返回值。

    如果你想摆脱这两个问题 主要的

    我想不出一种语言没有 主要的 . 例如,在Java中,有一个外部提供的类名 主要的 调用静态函数。在Python中,有 __main__ 模块。在 perl 这是您在命令行中指定的脚本。

        5
  •  2
  •   Ferruccio    15 年前

    如果正在构造多个全局对象,则无法保证哪个构造函数将首先运行。

        6
  •  2
  •   Steve Townsend    15 年前

    如果您正在构建静态或动态库代码,则不需要定义 main 你自己,但是你还是会运行一些程序。

        7
  •  2
  •   Jon    15 年前

    如果您是为windows编写代码,请执行 做这个。

    完全从全局对象的构造函数中运行应用程序可能会很好地运行一段时间,但迟早你会调用错误的函数,最终导致程序在没有警告的情况下终止。

    1. 全局对象构造函数在C运行时启动期间运行。
    2. C运行时启动代码在C运行时DLL的DLLMain期间运行
    3. 在DLLMain期间,您持有DLL加载程序锁。
    4. 在已经持有DLL加载器锁的情况下尝试加载另一个DLL会导致进程迅速死亡。

    将整个应用程序编译成一个可执行文件并不能帮你省钱——许多Win32调用都有可能悄悄加载系统dll。

        8
  •  0
  •   Ofir    15 年前

    在有些实现中,全局对象是不可能的,或者对于此类对象不可能使用非平凡的构造函数(特别是在移动和嵌入式领域)。