В обсуждении предыдущего поста один комментатор заявил:
Когда объект создаётся первый раз, его счётчик ссылок должен быть равен 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 можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.