[英]Does an object of a subclass also allocate an object of the parent class?
Class A
{
int X {get;set;}
}
Class B : A
{
int Y {get;set;}
}
B myB;
為爭辯說,類型A的對象是4個字節,類型B的對象是4個字節。
B被分配/初始化/構造,其內容存儲在堆中。
是B的大小是8個字節,同時帶有X和Y的指針,還是兩個4個字節的對象?
是否仍然會導致兩個對象,如下所示?
Class A {}
Class B
{
A myA;
}
B myB;
如果這些情況導致了不同的數據,我是否可以假定繼承使用的堆分配要比構造少,這是因為額外的包含對象已分配了數據,並且堆將數據分配到了包含對象之外?
我是否可以得出結論,構造比繼承效率要低得多?
澄清:初始化子類類型的對象時,該對象的父類的屬性的字節是否與子類的屬性的字節一起存儲? B是存儲指向A的指針還是直接存儲A的屬性? 如果它存儲一個指向A實例的指針,那么它將在內存中就像具有A類型的屬性一樣。
另外:在第一示例中,據說B的數據也包含A的數據。 分配子類對象的內存時,有兩種分配方式還是一種分配方式?
在第二個示例中,您分配了兩次內存:一次是為類型A的所包含屬性對象分配的,另一次是為對象類B分配的,因此我假設您有兩個不同的內存分配和兩個不同的對象並帶有一個指向A內B.
如果一個子類僅為其所有屬性及其所有繼承屬性的整個大小分配一次內存,並內聯存儲所有這些信息,那么,這不是使它成為為一個對象分配多個對象的一種更好的內存分配方法嗎?
假設您在x86體系結構上運行.NET,則空對象的開銷為8字節(最小大小為12字節)。 這意味着一個new object()
將占用12個字節的內存。
考慮以下類結構:
public class Parent // overhead 8 bytes
{
int a, b, c, d; // int 4 bytes * 4 = 16 bytes
}
public class Child : Parent // inherits everything
{ }
var parent = new Parent(); // 24 bytes
var child = new Child(); // 24 bytes [NOTE: this is a SINGLE object of type Child, not two objects Parent+Child]
它們是相同大小的對象,因為Child
未聲明任何其他字段。
改為使用以下類結構:
public class Composable // overhead 8 bytes
{
int a, b, c, d; // int 4 bytes * 4 = 16 bytes
}
public class Composite // overhead 8 bytes
{
Composable c = new Composable(); // 4 bytes (reference on the heap)
}
var composable = new Composable(); // 24 bytes
var composite = new Composite(); // 36 bytes (24 + 12) [NOTE: this creates TWO different objects, with one referencing the other]
由於兩個原因,開銷會稍高一些:創建新對象的“空對象”開銷和為堆棧上的內部對象分配的引用。
回答您的問題:是的,組合需要比繼承多一點的內存,但是數量實在太少了,您不必擔心。
初始化子類類型的對象時,該對象的父類的屬性的字節是否與子類的屬性的字節一起存儲?
是的,正如繼承示例清楚地顯示的那樣:它們是一個對象,其中包含其類和任何繼承的類的所有字段。
據說在第一示例中,B的數據也包含A的數據。 分配子類對象的內存時,有兩種分配方式還是一種分配方式?
一次分配
在第二個示例中,您分配了兩次內存:一次是為類型A的所包含屬性對象分配的,另一次是為對象類B分配的,因此我假設您有兩個不同的內存分配和兩個不同的對象並帶有一個指向A內B.
您幾乎是正確的,僅當您真正創建該對象時才進行第二次分配。 在您的示例中,您僅聲明一個字段A myA
:它占用4個字節的內存(在x86中),並且A myA
用4個字節。 創建該對象時它將占用更多內存(例如A myA = new A()
)
如果一個子類僅為其所有屬性及其所有繼承屬性的整個大小分配一次內存,並內聯存儲所有這些信息,那么,這不是使它成為為一個對象分配多個對象的一種更好的內存分配方法嗎?
組成VS繼承有不同的特點,我不相信有任何真實世界的影響,但肯定的,繼承然后稍微占用更少的內存組成。
初始化子類類型的對象時,該對象的父類的屬性的字節是否與子類的屬性的字節一起存儲?
應該是。 C#本身並沒有對內存模型指定太多(如果有的話;我對規范不是很熟悉)。 我相當確定這是實現定義的。 但是,由於以下幾個原因,包含引用其Base
的完全獨立實例的Derived
實例沒有意義:
如果Base
是抽象的,則內存模型不僅允許,而且實際上需要創建抽象類的實例。
在IL級別上,可以將指向Derived
托管指針(實際引用)放置在期望指向Base
的托管指針的任何位置。 現在,我們可以排除運行時執行某種轉換(例如,引用該Base
實例)的原因,因為那樣會丟失RTTI。 但是我們知道我們可以在運行時將Base
引用轉換為Derived
實例,因此它一定不能修改指針。
但是,當嘗試通過Derived
實例訪問和修改Base
字段時,這會引起皺紋。 在這種情況下,CLR必須將字段信息附加到實例v表。 當然不是不可能,但是到那時CLR實現者將竭盡全力使自己面臨更多困難。 據我所知,以這種方式執行操作並沒有真正的好處(除非您希望能夠在運行時更改繼承層次結構,而C#和CLR都不允許這樣做)。
通過添加另一層間接尋址(這可以通過僅在Derived
實例中直接包含Base
字段而避免)來避免很多無意義的性能損失。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.