为什么Assigned对未初始化的变量返回true?

use*_*858 6 delphi variables null pointers

我看了很多帖子在论坛上指点一下,Assigned功能,Free性能,FreeAndNil功能等......我已经知道自由功能不去除指针引用到分配的对象,FreeAndNil做它...所有的帖子我看了对待这个问题的考虑Create方法已经被执行,或者换句话说,考虑已经创建的对象.

我的问题是:为什么Assigned函数对于未初始化的对象变量返回true?

举个例子:

procedure TForm1.FormCreate(Sender: TObject);
var
  Qry: TADOQuery;    
begin
  if Assigned(Qry) then
    ShowMessage('Assigned')
  else
    ShowMessage('Unassigned');

  Qry := TADOQuery.Create(nil);

  if Assigned(Qry) then
    ShowMessage('Assigned')
  else
    ShowMessage('Unassigned');
end;
Run Code Online (Sandbox Code Playgroud)

该示例显示"已分配"两次!

结论:在Qry声明之后,在创建方法执行之前,指针Qry就不是NIL!

如果我把Qry := nil;第一行放到上面的程序中,一切正常......它显示'未分配'和'已分配'.

为什么??

有没有什么安全的方法可以知道类变量是否已经执行了create方法?

Dav*_*nan 7

您的变量是局部变量,因此未初始化.它可以包含任何值.

文件说:

在Win32平台上,局部变量的内容是未定义的,直到为它们分配值.

请注意,作为实现细节,管理某些类型,甚至初始化托管类型的局部变量.托管类型的示例包括:字符串,接口,动态数组,匿名类型和变体.

你问:

有没有什么安全的方法可以知道类变量是否已经执行了create方法?

如果该变量是局部变量,则答案为否.程序员有责任落在你身上.在实践中,它很少成为问题,因为良好的代码具有较短的程序,这使得您更难以滑倒.即使你这样做,编译器也会一直警告你.

其他类型的变量(如类字段和全局变量)已初始化.


Rod*_*igo 5

因为在创建指针时,它带有该内存位置中的任何垃圾值。如果你想在里面写 NIL,它需要一些 CPU 周期,我认为 Delphi 不会自动完成,因为你可能想要更快的东西。在您的示例中,为什么将 NIL 分配给一个变量,如果不久之后您要在其中放入另一个值?

  • 我不知道为什么你被否决了(两次)。 (2认同)

Dis*_*ned 5

Assigned功能的文档(强调我的):

使用Assigned确定P引用的指针或过程是否为nil.P必须是指针或过程类型的变量引用.Assigned(P)对应于指针变量的测试P <> nil,对于程序变量对应于@P <> nil.

如果P为nil,则赋值返回false,否则返回true.

注意:Assigned无法检测到悬空指针 - 也就是说,不是nil但不再指向有效数据的指针.例如,在Assigned的代码示例中,Assigned将不会检测P无效的事实.

Assigned功能有效地实现为:

function Assigned(const P): Boolean;
begin
  Result := Pointer(P) <> nil;
end;
Run Code Online (Sandbox Code Playgroud)

所以函数并没有真正检查值是否真的被赋值.相反,它正在检查被分配的副作用.

  • 因此,如果分配了函数,则保证返回该函数.True
  • 但如果值未初始化,则行为未定义.基本上,因为一个未初始化值从先前的操作遗留下来的垃圾值,它可能,或者可能不会.

另一件需要注意的是,Assigned无法确定其价值的有效性.例如,即使基础对象不再有效,也会Assigned返回以下调用True.

var
  LObject: TObject;
begin
  LObject := TObject.Create;
  LObject.Free;
  if Assigned(LObject) then ShowMessage('Still assigned!?');
end;
Run Code Online (Sandbox Code Playgroud)

编辑:附录

回答你问题的第二部分.

有没有什么安全的方法可以知道类变量是否已经执行了create方法?

没有安全的方法来确定是否已创建对象实例.(也没有办法可靠地确认它还没有被销毁.)

但是,您可以遵循一些惯例(和良好实践)来帮助您.

首先请注意,如果某些内容是创建的,那么您应该只是"不确定",如果它是该段代码的故意特征.例如,如果您打算将对象"延迟初始化".

  • 我在这里要说的是:永远不要检查Assigned,因为你担心可能存在阻止它被分配的错误.
  • 这不仅无法可靠地执行,而且使代码过于复杂......这增加了错误的可能性.
  • 此外,如果您发现某些事情意外没有分配,那么您可以做些什么呢?忽略它只会毫无意义.此外,这是不好的说法:"好的,然后我将创建对象".因为那时你在多个地方复制创作逻辑.
  • 基本上你应该尝试让你的程序的每一部分都正确 - 不要让你的程序试图在任何地方仔细检查自己.

所以现在我们(希望)同意你只检查是否创建了某些内容,如果你故意选择创建的是可选的.你这样做如下:

  • 首先,确保变量/字段引用初始化nil.因此,保证分配一个值,这意味着不创建对象.(是的,命名有点扭曲.)
  • 您可以将可变/字段引用设置为对象的新实例,或者通过复制已存在对象的另一个引用来设置它.(注意现有的引用也可能是,但这不会导致任何问题.)
  • 如果您曾销毁该对象(或者甚至只想停止从该引用中使用它),请将您的变量/字段引用再次设置为nil.
  • 注意:Delphi已经初始化了新类的成员字段.所以那些不需要特别注意.