http://www.blogjava.net/huyi2006/articles/180169.html
回调函数是一个程序员不能显式调用的函数;通过将回调函数的地址传给
调用者从而实现调用。回调函数使用是必要的,在我们想通过一个统一接口实现不
同的内容,这时用回掉函数非常合适。比如,我们为几个不同的设备分别写了不同
的显示函数:void TVshow(); void
ComputerShow(); void NoteBookShow()...等
等。这是我们想用一个统一的显示函数,我们这时就可以用回掉函数了。void
sho
w(void (*ptr)()); 使用时根据所传入的参数不同而调用不同的回调函数。
不同的编程语言可能有不同的语法,下面举一个c语言中回调函数的例子,
其中一个回调函数不带参数,另一个回调函数带参数。
例子1:
//Test.c
#include <stdlib.h>
#include
<stdio.h>
int Test1()
{
int i;
for (i=0; i<30; i++)
{
printf("The %d th charactor is: %c\n", i, (char)('a' + i%26));
}
return 0;
}
int Test2(int num)
{
int i;
for (i=0; i<num; i++)
{
printf("The %d th charactor is:
%c\n", i, (char)('a' + i%26));
}
return 0;
}
void
Caller1(void (*ptr)())//指向函数的指针作函数参数
{
(*ptr)();
}
void
Caller2(int n, int (*ptr)())//指向函数的指针作函数参数,这里第一个参数是为指向函数的指针服务的,
{
//不能写成void Caller2(int (*ptr)(int
n)),这样的定义语法错误。
(*ptr)(n);
return;
}
int main()
{
printf("************************\n");
Caller1(Test1);
//相当于调用Test2();
printf("&&&&&&************************\n");
Caller2(30, Test2); //相当于调用Test2(30);
return 0;
}
以上通过将回调函数的地址传给调用者从而实现调用,但是需要注意的是带
参回调函数的用法。要实现回调,必须首先定义函数指针。函数指针的定义这里稍
微提一下。比如:
int (*ptr)();
这里ptr是一个函数指针,其中(*ptr)的括号不能省略,因为
括号的优先级高于星号,那样就成了一个返回类型为整型的函数声明了
本文链接
- 实现计算逻辑的分离
- 使代码更清晰(通过使用函数名)
C++里,如果程序员没有显式的定义默认构造函数(default constructor),编译器会在需要的时候生成一个,也就是隐式地声明出来。
隐式声明的默认构造函数有两种,一种是trivial(无用的) constructor,什么都不做;另一种是nontrival constructor,编译器合成的是后者。
在四种情况下,编译器需要合成nontrival constructor:
1. 带有"Default Constructor"的成员类对象
class B {public: A a;};
int f()
{
B b; //此处须调用B的默认构造函数
}
因为B::a是一个member object,且其class A拥有default constructor,所以编译器在需要调用B的默认构造函数时,会为B生成如下的默认构造函数
{
// C++伪码
a.A::A(); // 调用a的默认构造函数
}
但需要注意的是,编译器产生的默认构造函数不会初始化基本类型的成员变量。
如果class B中有int类型的变量,其值不会被默认构造函数初始化为0。
还有一种情况,如果class B已经拥有一个默认构造函数,但其中并未对其对象成员进行初始化,如下例:
B::B() {num = 0}
对于这种情况,编译器会扩张已有的constructor,将对象成员的构造过程安插在user code前,像这样:
{
a.A::A(); // 插入的compiler code
num = 0; // user code
}
另外还需注意的是,编译器所安插的代码会按照“对象在class中的声明顺序”来调用各个constructor,在user code前。
2. "带有Default Constructor"的Base Class
如果一个class派生自一个“带有default constructor”的base class,那个这个class的default constructor需要被编译器合成出来,以调用上一层base class的default constructor。
很多人都了解“子类在调用构造函数时,会先调用父类的构造函数”,这正是因为编译器在我们定义的constructor中进行了上述扩展。
3. "带有Virtual Function"的Class
虚函数表(vtbl)的相关实现机制超出了本文的范围,读者可找相关文章或书藉来学习。
如果class或继承的base class中声明了virtual function(虚函数),编译器会对default constructor进行扩张:
1. 为class产生一个virtual function table。
2. 为每个class object创建一个额外的point member(vptr),指向相应的virtual function table。
此外,编译器还会对“虚函数的调用操作”进行修改,如b.f(),会被改为:
// 1表示函数f()在vtbl中的索引值,(b.vptr[1])返回了函数f()的地址。
// &b表示交给f()函数调用的this指针,这里涉及到了name magling的知识,读者可自行搜索。
4. "带有一个Virtual Base Class"的Class
虚基类(virtual Base Class)指多继承时,某个base class可能会出现在多个继承路径上。为了防止该基产生多个拷贝,可将基类的继承声明为虚拟的,这样就只会继承基类的一份拷贝。
虽然这样base class只有一份拷贝,但在多态环境下,编译器无法固定”经由某个指针类型而存取的base class中的成员”的实际偏移位置,因为其实际类型是可以改变的。
cfont对这个问题的解决方法是:在derived class object的每一个virtual base class中安插一个指针__vbcX,所有经由引用或指针对virtual base class的操作都可以通过该指针来完成。
而对__vbcX的初始化,正是编译器在default constructor中进行的扩张。
对于这4种情况以外的,且没有声明default constructor的class,我们说它们拥有的是trivial default constructors,实际上它们并没有被合成出来。
所以,“任何class如果没有定义default constructor,都会被生成一个出来”这种说法是错误的。
最后还有一点要注意,生成出来的nontrivial default constructor只对类对象进行初始化,或基本类型(int, double, string)的成员变量还需要程序员来显式的进行初始化。
本文链接