以下项目无法以“访问冲突”错误运行。我使用Delphi XE2 Update 3。
program Project1;
{$APPTYPE CONSOLE}
type
TTestClass = class
public
class procedure Test;
end;
var
TestClass: TTestClass;
class procedure TTestClass.Test;
begin
end;
begin
TestClass.Test;
end.
如果我将class procedure Test
标记为“静态”,则没有问题。这是'设计'吗?
P.S。:这是我的错,对我感到羞耻。
答案 0 :(得分:7)
是的,你目睹的是正确的。
非静态类方法与实例方法一样,具有隐藏的Self
参数。对于类方法,它引用类引用。这就像编译器将您的方法转换为:
type
TTestClassClass = class of TTestClass;
procedure TTestClass_Test(Self: TTestClassClass);
当您在非类接收器(即对象引用)上调用类方法时,编译器会插入对ClassType
的调用,以使用运行时对象的类型,如下所示:
TTestClass_Test(TestClass.ClassType);
ClassType
方法获取对象的VMT的地址,但您的变量不引用任何VMT。您的变量是空指针或未初始化,因此如果您很幸运,尝试取消引用它以读取VMT地址会导致访问冲突。 (如果你运气不好,它会取消引用地址,并且地址恰好位于程序地址空间的其他位置,并且结果被解释为VMT指针,即使它不是。)
仅对类引用或有效对象引用调用类方法。
TTestClass.Test;
如上所述在类引用“literal”上调用它时,编译器已经知道第一个参数的值并转换调用,如下所示:
TTestClass_Test(TTestClass);
答案 1 :(得分:2)
听起来很合理。 TestClass是零。您不能在非实例上调用非静态类方法:
'可以通过类引用或对象引用来调用类方法。当通过对象引用调用它时,对象的类变为Self'的值 - 没有对象,没有类。
'类静态方法可以在没有对象引用的情况下访问' - 由于对类方法的直接静态调用,与TestClass实例无关。
答案 2 :(得分:1)
您正在调用nil引用上的方法。甚至类方法使用它们被调用的对象来确定运行时变量的实际类型。你必须这样做
TestClass := TTestClass.Create;
要实际创建该类的实例,那么
FreeAndNil(TestClass);
解除分配。
如果你这样做
TTestClass.Test;
然后在编译时解析调用,因为它不依赖于变量的类型,所以它可以工作。
答案 3 :(得分:0)
问题是,虽然你有一个TTestClass的声明变量,但你没有实例化。您需要在使用Test方法之前调用Create,否则它将失败(除了静态,不需要实例化整个类)。所以你的主要代码是
begin
TestClass := TTestClass.Create;
TestClass.Test;
TestClass.Free;
end.
答案 4 :(得分:0)
以下在XE中完美运行,它似乎是你的Delphi版本中的一个错误,你的代码是正确的,只要它不在类中调用“私有”变量而不是实例,但我认为你已经明白了。
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TTest = class
public
class procedure test;
end;
{ TTest }
class procedure TTest.test;
begin
Writeln('hello');
end;
begin
try
TTest.test;
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.