В обсуждении предыдущего поста один комментатор заявил:
Когда объект создаётся первый раз, его счётчик ссылок должен быть равен 0, а _AddRef должна вызываться позже в какой-то момент (наверное, через QueryInterface).(прим.пер.: это не так в Delphi; счётчик ссылок равен 1 во время создания объекта - он становится равен 0 лишь в самом конце, когда вы сохраняете ссылку в объект, а не интерфейс)
Если вы создаёте объект со счётчиком ссылок равным нулю, то вы играете со спичками. Для начала, когда объект создаётся, его счётчик ссылок не должен быть равен нулю - ведь ссылку на объект имеет тот, кто создаёт объект! Вспомните правило COM для ссылок: если функция возвращает ссылку (обычно ссылку на интерфейс), то счётчик ссылок увеличивается с учётом созданной ссылки. Если вы посмотрите на конструктор как на функцию, то он должен будет возвращать увеличенный счётчик ссылок.
Если вы предпочитаете играть со спичками, то вы можете обжечься с аналогичным кодом:
// Статический метод создания объекта class function TMyObject.Create(riid: REFIID; out ppvObj): HRESULT; var pObj: TMyObject; begin Pointer(ppvObj) := nil; pobj := TMyObject.Create; Result = pobj.Initialize; // опасно! if SUCCEEDED(Result) Result = pobj.QueryInterface(riid, ppvObj); if Failed(Result) then pObj.Free; end;Заметьте, что вы инициализируете объект пока его счётчик ссылок равен 0. Этим вы оказываетесь в той же красной зоне, что и с деструктором - и у вас появляются те же проблемы:
function TMyObject.Load: HRESULT; var spstm: IStream; spows: IObjectWithSite; begin Result := GetLoadStream(spstm); if SUCCEEDED(Result) then begin Supports(spstm, IObjectWithSite, spows); if Assigned(spows) then spows.SetSite(Self); Result := LoadFromStream(spstm); if Assigned(spows) spows.SetSite(nil); end; end; function TMyObject.Initialize: HRESULT; begin Result := Load; end;Объект, который сохраняет себя во время уничтожения, вполне может и загружать себя при создании. И тогда вы вляпываетесь в ту же самую проблему. Вызов
IObjectWithSite.SetSite(Self)
увеличит счётчик с нуля до 1, а затем вызов IObjectWithSite.SetSite(nil)
сбросит счётчик до нуля, приводя к ошибочному удалению объекта во время инициализации.Затем статический метод
TMyObject.Create
не осознает этого и продолжит выполнение, вызывая QueryInterface
, чтобы увеличить счётчик с нуля до единицы и вернуть интерфейс вызывающему. К сожалению, он будет делать это на объекте, который уже удалён.Вот что происходит, когда вы играетесь с объектами с нулевым счётчиком ссылок: он может исчезнуть в любой момент, когда вы ослабляете контроль. Объекты следует создавать со счётчиком ссылок равным единице, а не нулю.
Прим.пер.: здесь ещё была куча текста по ATL - удалил без перевода, как не представляющее для меня интереса.
Комментариев нет:
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.