Подтвердить что ты не робот

Вам действительно нужен main() в С++?

Из того, что я могу сказать, вы можете начать все действия в конструкторе при создании глобального объекта. Так вам действительно нужна функция main() в С++ или это просто наследие?

Я могу понять, что это можно считать плохой практикой. Я просто спрашиваю из любопытства.

4b9b3361

Ответ 1

Если вы хотите запустить свою программу на размещенной версии С++, вам понадобится функция main. Это как раз то, как определяются вещи. Вы можете оставить его пустым, если хотите, конечно. С технической стороны компоновщик хочет разрешить символ main, который используется в библиотеке времени выполнения (который не имеет понятия о ваших особых намерениях опустить его - он все еще испускает вызов). Если в стандарте указано, что main является необязательным, то, конечно, реализации могут придумать решения, но это должно произойти в параллельной юниверсе.

Если вы перейдете с "Выполнение начинается в конструкторе моего глобального объекта", остерегайтесь того, что вы задаете себе множество проблем, связанных с порядком конструкций объектов области пространства имен, определенных в разных единицах перевода (Итак, что такое запись точка? Ответ: у вас будет несколько точек входа, и какая точка входа будет выполнена в первую очередь не указана!). В С++ 03 вы даже не гарантируете, что cout правильно сконструирован (в С++ 0x у вас есть гарантия, что он до того, как какой-либо код попытается использовать его, если есть предыдущий include <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 может затем определить такую ​​функцию на этой платформе.

Ответ 2

Да! Вы можете покончить с основным.

Отказ от ответственности: вы спросили, возможно ли это, а не если это нужно сделать. Это абсолютно не поддерживаемая, плохая идея. Я сделал это сам по причинам, в которые я не попаду, но я не рекомендую это. Моя цель не избавилась от основной, но она тоже может это сделать.

Основные шаги следующие:

  • Найти crt0.c в исходной папке исходного кода вашего компилятора.
  • Добавьте crt0.c в свой проект (копия, а не оригинал).
  • Найти и удалить вызов на main из crt0.c.

Получение его для компиляции и ссылки может быть затруднено; Насколько сложно зависит от того, какой компилятор и какая версия компилятора.

Добавлен

Я просто сделал это с Visual Studio 2008, так что вот точные шаги, которые вы должны предпринять, чтобы заставить его работать с этим компилятором.

  • Создайте новое консольное приложение С++ Win32 (нажмите "Далее" и отметьте Empty Project).
  • Добавить новый элемент. Файл С++, но назовите его crt0.c (not.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/С++ → Общие → Дополнительные каталоги Include = "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src".
  • Установить C/С++ → Препроцессор → Определения препроцессора = _CRTBLD.
  • Нажмите "ОК".
  • Щелкните правой кнопкой мыши имя проекта и выберите "Свойства".
  • Установить C/С++ → Генерация кода → Библиотека времени выполнения = 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;

Добавлен

Я удалил суперплотный вызов exit и рекламный ролик о жизни, так как я думаю, что мы все способны понять последствия удаления main.

Ultra Necro

Я только что наткнулся на этот ответ и прочел как его, так и возражения Джона Диблинга ниже. Было очевидно, что я не объяснил, что делает вышеуказанная процедура, и почему это действительно удаляет основной из программы целиком.

Джон утверждает, что "в CRT всегда есть главная". Эти слова не совсем правильны, но дух утверждения есть. Main не является функцией, предоставляемой CRT, вы должны добавить ее самостоятельно. Вызов этой функции содержится в функции точки входа CRT.

Точкой входа каждой программы C/С++ является функция в модуле с именем crt0. Я не уверен, что это соглашение или часть спецификации языка, но каждый компилятор C/С++, с которым я столкнулся (что очень много), использует его. Эта функция в основном выполняет три функции:

  • Инициализировать CRT
  • Основной вызов
  • Срыв вниз

В приведенном выше примере вызов является _tmain, но это макрос магии, позволяющий использовать различные формы, которые "main" может иметь, некоторые из которых являются VS специфическими в этом случае.

Что делает вышеприведенная процедура, так это удаление модуля crt0 из CRT и замена его на новый. Вот почему вы не можете использовать DLL Runtime, в этой DLL уже есть функция с тем же именем точки входа, что и добавляемое (2). Когда вы статически связываете, CRT представляет собой коллекцию .lib файлов, а компоновщик позволяет полностью переопределять модули .lib. В этом случае модуль с только одной функцией.

Наша новая программа содержит запасной CRT, минус его модуль CRT0, но с модулем CRT0 нашего собственного создания. Там мы удаляем вызов на главный. Итак, нет нигде!

(2) Вы можете подумать, что вы можете использовать DLL во время выполнения, переименовав функцию точки входа в файл crt0.c и изменив точку входа в настройках компоновщика. Однако компилятор не знает об изменении точки входа, а DLL содержит внешнюю ссылку на "главную" функцию, которую вы не предоставляете, поэтому она не будет компилироваться.

Ответ 3

В общем случае для приложения требуется точка входа, а main - это точка входа. Тот факт, что инициализация globals может произойти до main, в значительной степени не имеет значения. Если вы пишете консольное приложение или приложение с графическим интерфейсом, у вас должен быть main для его связи, и только хорошая практика заключается в том, чтобы эта процедура отвечала за основное исполнение приложения, а не использовала другие функции для причудливых непреднамеренных целей.

Ответ 4

Ну, с точки зрения стандарта С++, да, это все еще требуется. Но я подозреваю, что ваш вопрос отличается от этого.

Я думаю, что делать это так, как вы думаете, вызовет слишком много проблем.

Например, во многих средах возвращаемое значение из main указывается как результат состояния от запуска программы в целом. И это было бы очень сложно воспроизвести от конструктора. Некоторая часть кода все равно может вызвать exit, но это похоже на использование goto и пропустит уничтожение чего-либо в стеке. Вы можете попытаться исправить ситуацию, выделив вместо этого специальное исключение, чтобы создать код выхода, отличный от 0.

Но тогда вы все еще сталкиваетесь с проблемой определения порядка выполнения глобальных конструкторов, которые не определены. Это означает, что в любом конкретном конструкторе для глобального объекта вы не сможете делать никаких предположений о том, существует или нет какой-либо другой глобальный объект.

Вы можете попытаться решить проблему порядка конструктора, просто указав, что каждый конструктор получает свой собственный поток, и если вы хотите получить доступ к любым другим глобальным объектам, вам придется ждать переменную условия, пока они не скажут, что они построены. Однако это просто требует взаимоблокировок, и эти тупики будут очень трудно отлаживать. У вас также будет проблема с тем, какой поток, выходящий со специальным исключением "возвращаемое значение из программы", будет представлять собой реальное возвращаемое значение программы в целом.

Я думаю, что эти два вопроса являются убийцами, если вы хотите избавиться от main.

И я не могу думать о языке, который не имеет базового эквивалента main. Например, в Java есть внешнее имя класса, для которого вызывается статическая функция main. В Python есть модуль __main__. В perl есть script, который вы указываете в командной строке.

Ответ 5

Если у вас построено более одного глобального объекта, нет никаких гарантий относительно того, какой конструктор будет работать первым.

Ответ 6

Если вы создаете статический или динамический код библиотеки, вам не нужно определять main самостоятельно, но вы все равно будете работать в какой-либо программе, которая имеет его.

Ответ 7

Если вы кодируете окна, сделайте это не.

Запуск приложения целиком из конструктора глобального объекта может работать очень хорошо довольно долго, но рано или поздно вы вызовете неправильную функцию и закончите с программой, которая завершается без предупреждения.

  • Конструкторы глобальных объектов запускаются во время запуска среды выполнения C.
  • Код запуска C запущен во время DLLMain библиотеки времени выполнения C
  • Во время DLLMain вы держите блокировку загрузчика DLL.
  • Tring для загрузки другой DLL, когда уже держит блокировку загрузчика DLL, приводит к быстрой смерти для вашего процесса.

Компиляция всего вашего приложения в один исполняемый файл не спасет вас - многие вызовы Win32 могут спокойно загружать системные DLL.

Ответ 8

Существуют реализации, где глобальные объекты невозможны или где нетривиальные конструкторы невозможны для таких объектов (особенно в мобильных и встроенных сферах).