回复:lerit的关于对象中字段的初始化问题
谢谢lerit的对于我的博文《内存探寻1之——值类型和引用类型的内存分配机制》中提到的问题。这里先重述问题:
"你好,你的文章写得很好,清晰了很多以前比较模糊的概念,我想提个问题,上面你总结了自定义对象的过程,那三条是按照顺序的吗?
另外,下面这个类,是先实例化字段,还是先构造器,我从第三条中看出是“初始化构造器,对对象字段初始化”,可是好像是构造函数后执行了,请指教
public class c
{
public int a = 9;
public c()
{
a = 0;
}
}"
其实我对于字段的初始化以前没有在意,在这里谢谢你的评论,使我关注到了它。我的解释是:
我的分析:
首先:我理解你的问题主要是:类中的字段(ex,上例中的 a)的初始化问题! (呵呵,希望没有错~);
其次:我对于对象的内存分配顺序是(由博文《内存探寻1之——值类型和引用类型的内存分配机制》):
1.构造实例化对象中TypeHandler所指向的对象(可认为是Method Table),包括实现接口、静态字段、方法等,并提交至Loader Heap上;
2. 初始化实例的2个附加成员(TypeHandler和SyncBlockIndex),并且将TypeHandler指针指向Method Table;
3.初始化构造器,对对象字段初始化;
我想用一例子看一下效果:
2
3 //CopyRight: http://www.cnblogs.com/yangmingming
4
5 //Notes: 为简便,将类的建立,和实例化类放于一起
6
7 namespace ConsoleApplication1
8 {
9 public class TestClass
10 {
11 public int c ;
12
13 public TestClass(int cc)
14 {
15 c = cc;
16 }
17 }
18 }
19
20 class Program
21 {
22 static void Main(string[] args)
23 {
24 TestClass tc = new TestClass(5);
25 Console.WriteLine(tc.c);
26 }
27 }
28 //输出为:5
为说明方便,我们在15行设定断点,经调试,其界面为:
可见,如你所言,对象中字段在调用构造函数前,已经有值了!然而在这里我想我们的理解会有误差,具体分析如下:
一: 我上面所讲的"初始化构造器,对对象字段初始化",可以肯定的说,你理解的与我不同(呵呵,必须的~)。我的理解是——调用构造函数,而其主要作用是对其中字段初始化!(你的理解是先后顺序执行?!);
二:我认为可以认为类中字段的直接赋值(如你例中:public int a = 9;) 为在内存直接分配完毕后,给字段相应位置赋值,这或许可以被称为字段初始化(虽然我对这种方式不甚熟悉)。即:对字段的赋值是在我的上述对象内存分配过程3过程之前执行的!
三:从我的上例中看到,当未对字段赋初值时(实质上,系统出于C#的类型安全性要求会对字段类型分别赋初值0或null), 在调用构造函数前字段已经采取了初始赋值:0;
附:之所以会对我的说明产生误解(或者,我的说明不清晰)主要因为
1;在C++中,构造函数的作用即:为字段赋初值;
2:关于初始化,在C++中是不允许直接使用: public int a = 9;的,这样会报错。它采用了一种比较特殊的为字段初始化的方式(提供默认值-声明与定义分离,这里不阐述)。而C#中的字段初始化应在内存分配完成后直接完成的;
3:因为我虽然学习了C#,可能由于还没有在工作中更多实践,所以C++思想还很"丰富"的缘故(呵呵,尽快转变~);
最后,还是感谢lerit的问题,让我有了更多思考,希望以后继续交流!