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

时间:2014-01-16 17:41:16

标签: delphi variables pointers null

我在论坛上阅读了许多关于指针,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;

该示例显示“已分配”两次!

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

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

为什么?

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

3 个答案:

答案 0 :(得分:7)

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

documentation说:

  

在Win32平台上,内容为   在分配值之前,未定义局部变量   它们。

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

你问:

  

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

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

初始化其他类型的变量,如类字段和全局变量。

答案 1 :(得分:5)

Assigned函数的文档(强调我的):

  

使用Assigned确定P引用的指针或过程是否为nil。 P必须是指针或过程类型的变量引用。 分配(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;

因此该函数并未真正检查该值是否真正分配。相反,它正在检查被分配的副作用。

  • 如果被分配,该功能保证返回True
  • 但如果值未初始化,则行为未定义。基本上,由于未初始化的值具有以前操作中遗留的垃圾值,因此可能 nil ,或者可能

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

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

编辑:附录

回答你问题的第二部分。

  

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

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

但是,您可以遵循一些惯例(和良好做法)来帮助您。

首先请注意,如果某些内容被创建,那么您应该只是“不确定”,如果它是该段代码的故意特征。例如。如果你想让一个对象“懒惰初始化”。

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

所以现在我们(希望)同意你只检查是否创建,如果你故意选择创建是可选的。您可以按如下方式执行此操作:

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

答案 2 :(得分:3)

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