你真的需要C ++中的main()吗?

时间:2010-11-21 14:15:28

标签: c++

我可以告诉您,在创建全局对象时,可以在构造函数中启动所有操作。那么你真的需要一个C ++中的main()函数,还是仅仅是遗产?

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

8 个答案:

答案 0 :(得分:30)

如果要在托管C ++实现上运行程序,则需要main函数。这就是事情的定义方式。如果你想要的话,你可以把它留空。在技​​术方面,链接器想要解析运行时库中使用的main符号(它没有任何关于你的特殊意图的线索来省略它 - 它仍然只是调用它)。如果标准指定main是可选的,那么当然实现可以提出解决方案,但这需要在并行Universe中进行。

如果你使用“我的全局对象的构造函数中的执行开始”,请注意你自己设置了许多与不同翻译单元中定义的命名空间作用域对象的构造顺序相关的问题(那么的入口点?答案是:您将有多个入口点,并且首先执行的入口点是未指定的!)。在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 :(得分:4)

是的!您可以取消使用。

免责声明:您询问是否有可能,而不是应该这样做。这是一个完全不受支持的坏主意。我自己完成了这个,原因是我不会进入,但我不推荐它。我的目的不是摆脱主要,但它也可以做到这一点。

基本步骤如下:

  1. 在编译器的CRT源目录中查找crt0.c
  2. crt0.c添加到您的项目中(副本,而不是原始文件)。
  3. crt0.c找到并删除对main的调用。
  4. 让它进行编译和链接可能很困难;有多困难取决于哪个编译器和哪个编译器版本。

    <强>加

    我刚刚使用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 ++ - &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. 将以下代码粘贴到app.cpp并运行它。
    14. (*)您不能使用运行时DLL,您必须静态链接到运行时库。

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

      <强>加

      我删除了超级退出调用和关于生命的模糊,因为我认为我们都能够理解删除主要内容的后果。

      Ultra Necro

      我刚刚看到了这个答案,并在下面阅读了它和John Dibling的反对意见。很明显,我没有解释上述程序的作用以及为什么确实完全从程序中删除了main。

      约翰断言CRT中“总有一个主要”。这些话并不严格正确,但声明的精神是。 Main不是CRT提供的功能,您必须自己添加。对该函数的调用是在CRT提供的入口点函数中。

      每个C / C ++程序的入口点都是名为“crt0”的模块中的函数。我不确定这是一个约定或语言规范的一部分,但我遇到的每个C / C ++编译器(很多)都使用它。这个功能基本上做了三件事:

      1. 初始化CRT
      2. 致电主要
      3. 撕下
      4. 在上面的示例中,调用是_tmain,但这是一个宏魔术,允许'main'可以拥有的各种形式,其中一些在这种情况下是VS特定的。

        上述过程的作用是从CRT中删除模块“crt0”并将其替换为新模块。这就是为什么你不能使用Runtime DLL的原因,那个DLL中已经有一个函数,它的入口点名称与我们添加的那个相同(2)。静态链接时,CRT是.lib文件的集合,链接器允许您完全覆盖.lib模块。在这种情况下,只有一个功能的模块。

        我们的新程序包含股票CRT,减去其CRT0模块,但我们自己创建的CRT0模块。在那里我们删除对main的调用。所以在任何地方都没有主要的!

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

答案 2 :(得分:3)

一般来说,应用程序需要一个入口点,main就是入口点。全局变量初始化可能在main之前发生的事实几乎无关紧要。如果你正在编写一个控制台或GUI应用程序,你必须有一个main来链接它,这是一个很好的做法,让该例程负责应用程序的主要执行,而不是使用其他功能进行奇怪的无意的目的。

答案 3 :(得分:3)

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

我认为按照你想的方式做这件事会导致太多问题。

例如,在许多环境中,main的返回值作为整体运行程序的状态结果给出。而这很难从构造函数中复制出来。当然,有些代码仍然可以调用exit,但这似乎使用goto并且会跳过对堆栈中任何内容的破坏。您可以尝试通过设置特殊异常来解决问题,以生成0以外的退出代码。

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

你可以尝试通过说每个构造函数获得自己的线程来解决构造函数顺序问题,如果你想访问任何其他全局对象,你必须等待条件变量,直到它们说它们被构造。这只是要求死锁,而那些死锁将非常难以调试。您还有一个问题,即退出哪个特殊的'程序返回值'异常将构成整个程序的实际返回值。

如果你想摆脱main,我认为这两个问题是杀手。

我无法想到一种与main没有基本等价的语言。例如,在Java中,有一个外部提供的类名称,其中调用了main静态函数。在Python中,有__main__模块。在perl中,您可以在命令行中指定脚本。

答案 4 :(得分:2)

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

答案 5 :(得分:2)

如果你正在构建静态或动态库代码,那么你不需要自己定义main,但是你仍然可以在一些拥有它的程序中运行。

答案 6 :(得分:2)

如果您正在为Windows编码,请执行

完全从全局对象的构造函数中运行你的应用程序可能会工作很长时间,但是迟早你会调用错误的函数并最终得到一个在没有警告的情况下终止的程序。

  1. 全局对象构造函数在C运行时启动期间运行。
  2. C运行时启动代码在C运行时DLL的DLLMain期间运行
  3. 在DLLMain期间,您持有DLL加载程序锁。
  4. 在已经持有DLL加载程序锁定的同时加载另一个DLL会导致进程快速死亡。
  5. 将整个应用程序编译为单个可执行文件将无法保存您 - 许多Win32调用都有可能悄悄加载系统DLL。

答案 7 :(得分:0)

有些实现无法使用全局对象,或者这些对象无法实现非平凡的构造函数(特别是在移动和嵌入领域)。