Delphi - Почему TObject.InitInstance общедоступен?

Я немного новичок в Delphi, и этот вопрос просто мне любопытно. (Я также попытался использовать это случайно, только чтобы обнаружить, что я не должен.)

Если вы посмотрите на документацию для TObject.InitInstance он говорит вам не использовать его, если вы не переопределяете NewInstance, Метод также общедоступен. Почему бы не сделать его защищенным, если пользователь никогда не должен вызывать его?

3 ответа

Решение

Так как я был где-то, когда вся эта штука с Delphi началась в середине 1992 года, вероятно, есть несколько ответов на этот вопрос. Если вы посмотрите на исходную декларацию для TObject в Delphi 1, в TObject не было никаких защищенных / закрытых членов. Это произошло потому, что в самом начале разработки Delphi и в сочетании с введением исключений в язык исключения выделялись из кучи, отличной от других объектов. Это было происхождение функций NewInstance/InitInstance/CleanupInstance/FreeInstance. Переопределяя эти функции на ваших типах классов, вы можете буквально контролировать, где расположен объект.

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

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

На днях мы, вероятно, отправим код, который делает то, что я описал выше... сейчас это часть внутреннего "исследовательского" проекта.

О, как в стороне, а также немного урок истории... До выпуска Delphi 1 проект распределения / освобождения экземпляров исключений возвращался к использованию той же кучи, что и все другие объекты. Из-за общей коллективной ошибки предполагалось, что нам нужно выделить все экземпляры объекта Exception для "защиты" случая Out of memory. Мы рассуждали, что, если мы попытаемся вызвать исключение, потому что у менеджера памяти "недостаточно памяти", как мы можем выделить экземпляр исключения!? Мы уже знаем, что на данный момент нет памяти! Поэтому мы решили, что отдельная куча необходима для всех исключений... пока Чак Яздзевски или Андерс Хейлсберг (точно не помню, какой) не нашли простое, довольно умное решение... Просто предварительно выделите исключение нехватки памяти на старте! Нам по-прежнему нужно было контролировать, должно ли исключение когда-либо освобождаться (экземпляры исключений автоматически освобождаются после обработки), поэтому остался весь механизм NewInstance/FreeInstance.

Ну, никогда не говори никогда. В VCL слишком много вещей является приватным, а не виртуальным, так что мне нравится тот факт, что этот материал является публичным.

На самом деле это не обязательно для обычного использования, но в определенных случаях вы можете использовать его для массового размещения объектов. NewInstance резервирует немного памяти для объекта и затем вызывает InitInstance для его инициализации. Вы можете написать фрагмент кода, который выделяет память для большого количества объектов за один раз, а затем вызывает InitInstance для разных частей этого большого блока, чтобы инициализировать в нем разные блоки. Такая реализация могла бы стать основой для реализации шаблона с наименьшим весом.

Обычно вам вообще не нужна такая вещь, но приятно, что вы можете, если действительно хотите / нуждаетесь.

Как это устроено?

Самое интересное: конструктор в Delphi - это всего лишь метод. Сам метод Create не делает ничего особенного. Если вы посмотрите на это, это просто метод, как и любой другой. Это даже пусто в TObject!

Вы даже можете вызвать его для экземпляра (вызвать MyObject.Create вместо TMyObject.Create), и он вообще не вернет новый объект. Ключ находится в constructor ключевое слово. Это говорит компилятору, что перед выполнением метода TAnyClass.Create он должен также создать фактический экземпляр объекта.

Это строительство означает в основном вызов NewInstance, NewInstance выделяет часть памяти для данных объекта. После этого он вызывает InitInstance сделать некоторую специальную инициализацию этой памяти, начиная с очистки ее (заполнение нулями).

Выделение памяти является относительно дорогой задачей. Менеджер памяти (скомпилированный в ваше приложение) должен найти свободный кусок памяти и назначить его вашему объекту. Если памяти недостаточно, необходимо отправить запрос в Windows, чтобы получить еще. Если вам нужно создать тысячи или даже миллионы объектов, это может быть неэффективно.

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

Во всяком случае, это всего лишь гипотеза о причине. Может быть, нет никакой причины вообще. Я видел так много нерационально примененных уровней видимости в VCL. Может быть, они просто не думали об этом вообще.;)

Это дает разработчикам возможность создавать объекты, не использующие NewInstance (память из стека / пул памяти)

Другие вопросы по тегам