【问题标题】:How to construct object without allocating its memory?如何在不分配内存的情况下构造对象?
【发布时间】:2017-06-01 05:30:34
【问题描述】:

我希望包含在其他对象中的对象不必分配它们的内存。例如在 Delphi 中我可以这样写:

type
  TTest = class
    obj1: tobject1;
    obj2: tobject2;
    constructor Create;
    destructor Destroy; override;
  end;

constructor TTest.Create;
begin
  inherited;
  obj1 := tobject1.Create;
  obj2 := tobject2.Create;
end;

destructor TTest.Destroy;
begin
  obj1.Free;
  obj2.Free;
  inherited;
end;

类似的 C++ 代码如下所示:

class TTest {
    tobject1 obj1;
    tobject2 obj2;
};

我想关注的区别是在 Delphi 版本中有 3 个内存分配,而在 C++ 版本中只有 1 个。有没有办法在 Delphi 中为一个完整的对象及其子对象只分配一个内存,就像在 C++ 中一样?

更新:完整的 C++ 测试代码:

class tobject1 { public: int aaa; };
class tobject2 { public: int bbb; };

class TTest {
    tobject1 obj1;
    tobject2 obj2;
public:
    void doThing() { obj1.aaa = obj2.bbb; };
};

int main()
{
    {TTest * test = new TTest; test->doThing(); delete test; }
    {TTest test; test.doThing(); }

    return 0;
}

反汇编代码:

{TTest * test = new TTest; test->doThing(); delete test; }
013D182E  push        8  
013D1830  call        operator new (013D1299h)  
013D1835  add         esp,4  
013D1838  mov         dword ptr [ebp-0E4h],eax  
013D183E  mov         eax,dword ptr [ebp-0E4h]  
013D1844  mov         dword ptr [ebp-8],eax  
013D1847  mov         ecx,dword ptr [ebp-8]  
013D184A  call        TTest::doThing (013D133Eh)  
013D184F  mov         eax,dword ptr [ebp-8]  
013D1852  mov         dword ptr [ebp-0F0h],eax  
013D1858  push        8  
013D185A  mov         ecx,dword ptr [ebp-0F0h]  
013D1860  push        ecx  
013D1861  call        operator delete (013D104Bh)  
013D1866  add         esp,8  
{TTest test; test.doThing(); }
013D1869  lea         ecx,[ebp-18h]  
013D186C  call        TTest::doThing (013D133Eh)  

c++ 对象只需为包含子对象的新对象分配 1 次内存,如果将对象放入堆栈而不是堆中,则甚至需要 0 次内存分配。

更新:完整的 Delphi 测试代码:

program Project1;

type
  tobject1 = class
    aaa: longint;
  end;

  tobject2 = class
    bbb: longint;
  end;

  TTest = class
    obj1: tobject1;
    obj2: tobject2;
    constructor Create;
    destructor Destroy; override;
    procedure doThing;
  end;

constructor TTest.Create;
begin
  inherited;
  obj1 := tobject1.Create;
  obj2 := tobject2.Create;
end;

destructor TTest.Destroy;
begin
  obj1.Free;
  obj2.Free;
  inherited;
end;

procedure TTest.doThing;
begin
  obj1.aaa := obj2.bbb;
end;

var
  test: TTest;

begin
  test := TTest.Create;
  test.doThing;
  test.Free;
end.

反汇编分配代码:

Project1.dpr.35: test := TTest.Create;
0040A0F4 B201             mov dl,$01
0040A0F6 A19C924000       mov eax,[$0040929c]
0040A0FB E8DCF2FFFF       call TTest.Create
0040A100 A3F8F44000       mov [$0040f4f8],eax
Project1.dpr.36: test.doThing;
0040A105 A1F8F44000       mov eax,[$0040f4f8]
0040A10A E865F3FFFF       call TTest.doThing
Project1.dpr.37: test.Free;
0040A10F A1F8F44000       mov eax,[$0040f4f8]
0040A114 E8DBA1FFFF       call TObject.Free

反汇编创建代码代码:

Project1.dpr.16: obj1 := tobject1.Create;
004093F4 B201             mov dl,$01
004093F6 A144914000       mov eax,[$00409144]
004093FB E8C4AEFFFF       call TObject.Create
00409400 8B55FC           mov edx,[ebp-$04]
00409403 894204           mov [edx+$04],eax
Project1.dpr.17: obj2 := tobject2.Create;
00409406 B201             mov dl,$01
00409408 A1F0914000       mov eax,[$004091f0]
0040940D E8B2AEFFFF       call TObject.Create
00409412 8B55FC           mov edx,[ebp-$04]
00409415 894208           mov [edx+$08],eax

这个 Delphi 对象需要为一个包含 2 个子对象的新对象分配 3 次内存。是否可以使用对象而不是记录(它们根本不一样,因为它们不支持继承)来减少像 C++ 中那样的一个分配(如果放在堆栈上,甚至是 0)?

【问题讨论】:

  • c++ 版本也有三种内存分配。 tobject1tobject2 的默认构造函数在 TTest 构造函数中调用。 Delphi 没有这个机制,所以你必须手动创建对象。
  • 子对象调用默认的构造函数和析构函数,但不调用new和delete,c++版本只为整个对象组合分配一块内存。
  • 我从未尝试过,但您可以使用覆盖 TObject.NewInstance。但请注意,这不是 Delphi 的方式。
  • 另一种可能性是使用records,它是像C++ structs 这样的值类型,而delphi classes 是像C#/Java 那样的引用类型。
  • 你想要的对于类实例是不可行的。

标签: delphi


【解决方案1】:

在 Delphi 中,类是引用类型,因此在实例化时总是会产生一个堆位置。因此,您无法使用类实现您希望的内存布局。

相比之下,记录是值类型。如果您将两种嵌入类型放入记录中,那么内存将按照您的意愿进行布局,并且只有一个堆分配。

【讨论】:

  • 我认为您的第一段的意思是“不能”?我希望它可以像你的第二段那样做,但也可以使用嵌入类型的继承,但如果不是那么好吧。
  • 值的继承使您可以看到object slicing。是的,我更正了第一段,谢谢。
  • 您可以将类型声明为object 而不是classrecordobject 可以在堆栈上分配,并且嵌套对象都将在一个分配中,类似于 C++ 代码。但是,object 是为向后兼容而提供的旧语法,在这种情况下会首选 record 类型。
  • 几年前我最后一次尝试 type object 时,它们在 freepascal 中运行良好,但在 delphi 中有各种错误,所以我没想过再试一次。我会试试看,如果他们现在工作会报告。
  • 最小测试,但看起来对象类型有效,但真的很奇怪。它可以继承和访问父成员但不能覆盖虚函数,构造函数/析构函数不起作用,类函数不起作用。
【解决方案2】:

你在这里想错了。 C++ 知道构造函数和析构函数,就像 Delphi 一样,只是你没有在 C++ 定义中定义它们。

不,你不能分配一个类的实例而不为该实例分配内存。 这在 Delphi 和 C++ 之间也没有什么不同。

【讨论】:

  • 在c++版本中,当创建一个新的TTest对象时,整个对象只会分配一个内存,主对象和每个子对象都会调用构造函数。我想要 Delphi 中的这种行为。
  • @Mardulu:这不是 classes 在 Delphi 中的工作方式。我可能不应该提及它们,但是旧的 Turbo Pascal object 类型更像 C++ 类,可以用作值类型,但即使它们也不是自动构造的。请注意,这些旧式 object 类型已弃用,不再完全受支持。
猜你喜欢
  • 2011-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-02
  • 2021-09-11
  • 1970-01-01
  • 2014-12-30
相关资源
最近更新 更多