在设置为 nil 的对象引用上调用 Free 不应该在每次调用时都会抛出访问冲突吗?

发布于 2024-12-22 19:12:41 字数 980 浏览 1 评论 0原文

我从单元 DBXCommon.pas (在 Delphi XE 中)收到访问冲突。当我查看代码时,我看到如下内容(在感叹号处):

function TDBXConnectionFactory.GetConnection(const DBXContext: TDBXContext;
  const ConnectionProperties: TDBXProperties): TDBXConnection;
var
  ConnectionBuilder:  TDBXConnectionBuilder;
  DelegatePath:       TDBXDelegateItem;
  Connection:         TDBXConnection;
  CombinedProperties: TDBXProperties;
begin
  //...
  ConnectionBuilder := TDBXConnectionBuilder.Create;
  Connection        := nil;
  try
    //..lots of setting ConnectionBuilder properties
    ConnectionBuilder.FInputPassword := CombinedProperties[TDBXPropertyNames.Password];
    Connection := ConnectionBuilder.CreateConnection;
    Connection.Open;
    Result     := Connection;
!!  Connection := nil;
  finally
!!  Connection.Free;
    ConnectionBuilder.Free;
  end;
end;

但我在 DBXCommon.pas 中看到更多类似这样的构造(首先分配 Nil,然后分配 Free)。这是我不知道的一些构造,还是这真的导致每次调用这段代码时发生访问冲突?

I'm getting access violations from the unit DBXCommon.pas (in Delphi XE). When I look at the code I see things like the following (at the exclamation marks):

function TDBXConnectionFactory.GetConnection(const DBXContext: TDBXContext;
  const ConnectionProperties: TDBXProperties): TDBXConnection;
var
  ConnectionBuilder:  TDBXConnectionBuilder;
  DelegatePath:       TDBXDelegateItem;
  Connection:         TDBXConnection;
  CombinedProperties: TDBXProperties;
begin
  //...
  ConnectionBuilder := TDBXConnectionBuilder.Create;
  Connection        := nil;
  try
    //..lots of setting ConnectionBuilder properties
    ConnectionBuilder.FInputPassword := CombinedProperties[TDBXPropertyNames.Password];
    Connection := ConnectionBuilder.CreateConnection;
    Connection.Open;
    Result     := Connection;
!!  Connection := nil;
  finally
!!  Connection.Free;
    ConnectionBuilder.Free;
  end;
end;

But I see constructs like this (first assign Nil, then a Free) much more in DBXCommon.pas. Is this some construct I do not know, or is this really causing access violation every time this piece of code is called?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

┼── 2024-12-29 19:12:41

对空引用调用 Free 始终是安全的。查看 TObject.Free 的实现来了解原因。

此代码是工厂函数的示例。它的工作是创建一个类的新实例,但如果失败,它需要确保在抛出异常时不会泄漏半创建的实例,因此它调用Free。当确定它会成功时,它将结果的所有权转移给调用者。它仍然调用 Free,但如果它已经转移了所有权,那么它最终会在空引用上调用 Free,并且不会造成任何损害。这段代码就是转移所有权的:

Result := Connection;
Connection := nil;

编写工厂函数的方式将消除单独的Connection变量。我会直接在 Result 中构造结果,但如果出现异常则释放它,如下所示:

function TDBXConnectionFactory.GetConnection(const DBXContext: TDBXContext;
  const ConnectionProperties: TDBXProperties): TDBXConnection;
var
  ConnectionBuilder:  TDBXConnectionBuilder;
  DelegatePath:       TDBXDelegateItem;
  Connection:         TDBXConnection;
  CombinedProperties: TDBXProperties;
begin
  //...
  ConnectionBuilder := TDBXConnectionBuilder.Create;
  try
    //..lots of setting ConnectionBuilder properties
    ConnectionBuilder.FInputPassword := CombinedProperties[TDBXPropertyNames.Password];
    Result := ConnectionBuilder.CreateConnection;
    try
      Result.Open;
    except
      Result.Free;
      raise;
    end;
  finally
    ConnectionBuilder.Free;
  end;
end;

这具有相同的效果。

Calling Free on a null reference is always safe. Go look in the implementation of TObject.Free to see why.

This code is an example of a factory function. Its job is to create a new instance of a class, but if it fails, it needs to make sure it doesn't leak a half-created instance when it throws an exception, so it calls Free. When it's sure it's going to succeed, it transfers ownership of the result to the caller. It still calls Free, but if it's already transfered ownership, then it ends up calling Free on a null reference, and there's no harm done. This code is what transfers ownership:

Result := Connection;
Connection := nil;

The way I would write a factory function would do away with the separate Connection variable. I'd construct the result directly in Result, but free it if there were an exception, like this:

function TDBXConnectionFactory.GetConnection(const DBXContext: TDBXContext;
  const ConnectionProperties: TDBXProperties): TDBXConnection;
var
  ConnectionBuilder:  TDBXConnectionBuilder;
  DelegatePath:       TDBXDelegateItem;
  Connection:         TDBXConnection;
  CombinedProperties: TDBXProperties;
begin
  //...
  ConnectionBuilder := TDBXConnectionBuilder.Create;
  try
    //..lots of setting ConnectionBuilder properties
    ConnectionBuilder.FInputPassword := CombinedProperties[TDBXPropertyNames.Password];
    Result := ConnectionBuilder.CreateConnection;
    try
      Result.Open;
    except
      Result.Free;
      raise;
    end;
  finally
    ConnectionBuilder.Free;
  end;
end;

That has the same effect.

多孤肩上扛 2024-12-29 19:12:41

nil 引用上调用 Free 是安全的,因为它会检查 Self <> 的实现。 nil 在调用 Destroy 之前。请参阅Embarcadero 论坛中 Allen Bauer 的解释 引入了 TObject.Free。我在这里只引用相关的引用:

在 TObject 上引入非虚拟 Free 方法的唯一原因是在析构函数中用作以下内容的简单简写:

if FField <> nil then
  FField.Destroy;

It is safe to call Free on nil reference as it's implementation checks for Self <> nil before calling Destroy. See Allen Bauer's explanation in Embarcadero forum why TObject.Free was introduced. I include only the relevant quote here:

The sole reason for introducing the non-virtual Free method on TObject, was for use in destructors as a simple shorthand for:

if FField <> nil then
  FField.Destroy;
巴黎夜雨 2024-12-29 19:12:41

TObject.Free 基本上实现为 if Self <> nil 然后 Destroy,因此上面的代码不应引发任何异常。

TObject.Free is basically implemented as if Self <> nil then Destroy, so the code above should not raise any exception.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文