标签: cppstruct构造函数校园 | 分类: C/C_PlusPlus |
在网络协议、通信控制、嵌入式系统的C/C++编程中,我们经常要传送的不是简单的字节流(char型数组),而是多种数据组合起来的一个整体,其表现形式是一个结构体。
下面看看一道关于C++中笔试题:
// FileName:test.cpp
1: #include <stdio.h>
2: struct test
3: {
4: test(int n){
5: printf("test(%d)/n",n);
6: }
7: test(){
8: printf("test()/n");
9: }
10: void Fun(){
11: printf("Fun()/n");
12: }
13: };
14: int main()
15: {
16: test a(1);
17: a.Fun();
18: test b();
19: b.Fun();
20: return 0;
21: }
找出这个程序错误的一行?
答案很简单就是第19行,往下看就知道为什么啦!
结构是一种用关键字struct声明的自定义数据类型。与类相似,也可以包含构造函数,常数,字段,方法,属性,索引器,运算符和嵌套类型等,不过,结构是值类型。但是,C++的结构和类是有区别的:
1、结构的构造函数和类的构造函数不同。
a.结构不能包含显式的无参数构造函数。结构成员讲自动初始化为它们的默认值。
b.结构不能包含以下形式的初始值设定类:base(argument-list);
2、对于结构中的实例字段成员,不能在声明时赋值初始化。
3、声明了结构类型后,可以使用new运算符创建构造对象,也可以不使用new,如果不使用new,那么在初始化所有字段之前,字段将保持未赋值状态且对象不可用。
4、结构不支持继承,即一个结构不能从另一个结构或类继承,而且不能作为一个类的基类。但是,结构从基类OBJECT继承。结构也可以实现接口。
从编译角度来思考下:
摘自csdn: http://blog.csdn.net/fuadam/archive/2008/08/04/2766616.aspx
最近才知道struct和class的静态构造函数的触发规则是不同的,不像class在第一次使用类的时候触发静态构造函数。如果只访问struct实例的字段是不会触发静态构造函数调用的。通过测试发现当访问静态字段,struct本身的函数(静态和实例)和带参数的构造函数就会引起静态构造函数的执行。而调用默认构造和未覆写的基类虚函数是不会的。为什么呢?
让我们先来看看class和struct在调用构造函数时的区别。class使用newobj指令而struct使用initobj指令来构造对象。newobj在堆上申请一块内存并调用相应的构造函数进行初始化,然后将对象地址返回给计算栈。initobj则是从本地变量表中载入已经分配出来的struct实例然后初始化struct的各字段。这个初始化过程是CLR内部执行的,而不像class编译器会给class添加一个默认构造函数(这就是为什么struct不能给字段添加默认值的原因。但在类中如果给字段添加了默认值编译器就会自动在构造函数中添加字段赋值操作)。如果给struct中定义了一个有参数的构造函数,那么系统就不会使用initobj指令,而是直接用call指令调用带参数的构造函数。
我们最常见最常用的调用函数的指令是call和callvirt。对于静态函数使用call指令,对于class使用callvirt指令(不论class中的函数是不是虚的)。只有子类调用父类的函数的时候(避免递归调用)以及构造函数中(由编译器添加保证父类字段被初始化)使用call指令。而对于struct我们发现只要调用的函数是struct本身定义的都是使用call指令。call和callvirt指令的差别在于,call会把调用的函数当作静态函数看待,而不会关心调用当前函数时实例指针(this)是否为空。这就是struct调用函数时为什么都是call因为struct实例是不可能被置为null的。实际上class在调用非虚函数时实际上也是使用call的只是多做了一步验证——this是否为空,让我们来验证一下。