Конструктор формы vs Form_Load [дубликат]

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

Давайте немного приблизимся к конкретному для объяснения. Скажем, у меня есть следующие файлы:

  • foo.h объявляет интерфейс class MyClass<T>
  • foo.cpp определяет реализацию class MyClass<T>
  • bar.cpp использует MyClass<int>

. Отдельное средство компиляции. Я должен скомпилировать foo.cpp независимо от bar.cpp. Компилятор полностью выполняет всю сложную работу по анализу, оптимизации и генерации кода на каждом модуле компиляции; нам не нужно анализировать целую программу. Только компоновщик должен обрабатывать всю программу одновременно, и задача компоновщика значительно упрощается.

bar.cpp даже не нужно существовать при компиляции foo.cpp, но я все равно должен быть в состоянии связать foo.o Я уже имел вместе с bar.o Я только что выпустил, не перекомпилируя foo.cpp. foo.cpp может даже быть скомпилирован в динамическую библиотеку, распределенную где-то в другом месте без foo.cpp, и связан с кодом, который они пишут спустя годы после того, как я написал foo.cpp.

«Полиморфизм в стиле объектов» означает, что template MyClass<T> не является общим классом, который может быть скомпилирован в код, который может работать для любого значения T. Это добавит накладные расходы, такие как бокс, необходимо передать указатели на функции для распределителей и конструкторов и т. Д. Намерение шаблонов C ++ состоит в том, чтобы избежать необходимости писать почти идентичные class MyClass_int, class MyClass_float и т. Д., Но все же быть в состоянии закончить с компилируемым кодом, который в основном выглядит так, как если бы мы имели каждую версию отдельно. Таким образом, шаблон является буквально шаблоном; шаблон класса не класс, это рецепт создания нового класса для каждого T, с которым мы сталкиваемся. Шаблон не может быть скомпилирован в код, только результат создания экземпляра шаблона может быть скомпилирован.

Итак, когда foo.cpp скомпилирован, компилятор не может видеть bar.cpp, чтобы знать, что MyClass<int> необходимо. Он может видеть шаблон MyClass<T>, но он не может испускать код для этого (это шаблон, а не класс). И когда компилируется bar.cpp, компилятор может видеть, что ему нужно создать MyClass<int>, но он не может видеть шаблон MyClass<T> (только его интерфейс в foo.h), поэтому он не может его создать.

Если foo.cpp сам использует MyClass<int>, тогда код для него будет сгенерирован при компиляции foo.cpp, поэтому, когда bar.o связан с foo.o, они могут быть подключены и будут работать. Мы можем использовать этот факт, чтобы позволить конечный набор экземпляров шаблонов быть реализован в .cpp-файле, написав один шаблон. Но bar.cpp не может использовать шаблон в качестве шаблона и создавать его на всех типах, которые ему нравятся; он может использовать только ранее существовавшие версии шаблона, которые автор foo.cpp думал предоставить.

Вы можете подумать, что при компиляции шаблона компилятор должен «сгенерировать все версии», с теми, которые никогда не используются, отфильтровываются во время связывания. Помимо огромных накладных расходов и экстремальных трудностей, с которыми сталкивался такой подход, поскольку «модификаторы типа», такие как указатели и массивы, позволяют даже встроенным типам создавать бесконечное количество типов, что происходит, когда я расширяю свою программу добавив:

  • baz.cpp объявляет и реализует class BazPrivate и использует MyClass<BazPrivate>

Невозможно, чтобы это могло работать, если мы либо

  1. Необходимо перекомпилировать foo.cpp каждый раз, когда мы меняем любой другой файл в программе , в случае, если он добавил новый романный экземпляр MyClass<T>
  2. Требовать, чтобы baz.cpp содержал (возможно, через заголовок) полный шаблон MyClass<T>, чтобы компилятор мог генерировать MyClass<BazPrivate> во время компиляции baz.cpp.

Никто не любит (1), потому что системы компиляции целых программ принимают forever для компиляции и потому что это делает невозможным распространение компилированных библиотек без исходного кода. Итак, у нас есть (2).

34
задан Ryan 12 April 2010 в 16:55
поделиться

2 ответа

Код в конструкторе запускается немедленно, когда вы создаете форму, независимо от того, отображаете ли вы ее когда-либо. Код, выполняющийся в событии Form.Load , является обработчиком событий, поэтому вы можете иметь код в других классах (которые подписались на форму), запускающий там код. Точно так же вы можете (из формы) использовать метод Form.OnLoad для запуска кода.

Событие Load формы (и переопределяемый метод OnLoad, который часто является лучшим выбором в самой форме) запускается после инициализации формы. Это часто имеет преимущества, поскольку все элементы управления формы уже созданы, и, что более важно, весь макет формы выполнен.

20
ответ дан 27 November 2019 в 17:01
поделиться

Не используйте событие Load, переопределите метод OnLoad (). Это гарантирует, что все работает в предсказуемом порядке при наследовании от класса формы. Вы должны использовать его только для инициализации формы, для которой требуется знать размер фактической формы. Он может отличаться от размера проекта из-за масштабирования или предпочтений пользователя, а фактический размер неизвестен, пока не будет создано собственное окно.

Инициализация элементов управления в методе OnLoad возможна, но может быть очень медленной, особенно для ListView и TreeView. Если вы инициализируете их в конструкторе, они могут быть инициализированы массово при создании их собственных элементов управления Windows.

Одно специальное исключение: создание дочернего окна MDI всегда должно выполняться в OnLoad (), в соединительном коде есть ошибка, которая приводит к сбоям в панели MDI при создании дочернего окна в конструкторе.

27
ответ дан 27 November 2019 в 17:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: