如果你已经很久没有写C代码了,为了确保你的编译器还能工作,先运行下面的一个基础程序:
?
#include
int main(){
? ? ?printf("Hello World");
? ? ?return 0;
}
ok,我们现在开始加快步伐
?
我们将要创建三个继承于抽象Shape类的类,但是
C语言并没有类或继承的概念,但是有struct,下面就开始吧。
?
struct Square{
? ? int width;
? ? int height;
};
?
struct Circle{
? ? float radius;
};
?
struct Triangle{
? ? int base;
? ? int height;
};
没啥特别的,让我们更进一步的研究,将下面的代码放入main函数
?
printf("size of square is %d\n", sizeof(struct Square));
printf("size of Circle is %d\n", sizeof(struct Circle));
printf("size of Triangle is %d\n", sizeof(struct Triangle));
输出结果如下(不能担保,取决于平台):
?
size of cube is 8
size of circle is 4
size of triangle is 8?
?
如果用struct来模拟类,那类中的方法呢,可以采用函数指针,C语言对待函数指针就像其他成员一样,
?
考虑下面的函数:
?
void print_square( void ){
? ? printf("Hello Square\n");
}?
这个函数接受一个空的参数,返回也为空,意味着指向这个函数的指针是一个指向接受空参数返回空类型的函数,这里你可以声明这样的一个变量:
?
struct Square{
? ? int width;
? ? int height;
? ? //now for functions
? ? void (* print)( void );
? ? float (* area)( struct Square * this );
};?
关于C语言变量声明的规则,可以参考名著《C陷阱与缺陷》
?
print是一个指向参数为空、返回值为空的函数的指针,现在我们需要给Square一个面积函数,接受自身为参数,并且返回一个float类型来表示面积。读取的时候和print函数类似。
?
指针是很强大的,但是必须指向有用的东西,但是你怎么知道一个函数在内存中的地址,好消息来了,函数名就表示它在内存中的地址,下面举一个例子:
?
struct Square square;
square.print = print_square;?
函数指针的行为就像其他的成员一样,当我们创建一个Square类型的square,它的值包含垃圾值,我们需要手动地给它们赋一些正确的值。这样的工作需要一个构造器,C语言也没有,所以我们需要创建一个自己的构造函数。
?
void init_square( struct Square * square, int w, int h ){
? ? (*square).print = print_square;
? ? (*square).width = w;
? ? (*square).height = h;
? ? (*square).area = calc_square_area;
}?
这个构造器仅仅是另一个函数,需要传递改变square的值的参数,于是,参数必须是一个指向Square的指针以及需要传递的值
?
下面来测试一下我们所做的工作(以square为例):
?
#include
struct Shape{
? ? void (* print)( void );
? ? float (* area)( struct Shape * this ); ? ?
};?
?
struct Square{
? ? float width;
? ? float height;
? ? void (* print)( void );
? ? float (* area)( struct Square * this );
};?
?
void print_square( void ){
? ? printf("Hello Square\n");
}?
?
float calc_square_area(struct Square * this){
? ? return (*this).width * (*this).height;
}?
?
void init_square(struct Square * square, float w, float h ){
? ? (*square).width = w;
? ? (*square).height = h;
? ? (*square).print = print_square;
? ? (*square).area = calc_square_area;
}?
?
int main(void)
{
? ? struct Square square;?
? ? init_square( &square, 2.5, 2.6 );
? ? square.print();?
? ? printf("the area of the square is %.2f\n", square.area(&square));
? ? return 0;
}
我们必须创建一个Shape结构作为其他三种形状的父类,需要创建Shape结构的逻辑结构:
?
//抽象父类
struct Shape{
? ? void (* print)( void );
? ? float (* area)( struct Shape * this ); ? ?
};?
既然知道了从哪开始,接着就思考我们希望什么发生,假如你希望创建一个Square多次,初始化它
?
struct Square square;
init_square(&square, 2, 4);?
如果希望打印它,调用函数:
?
square.print();?
如果有一个指向Shape的指针试图打印square:
?
struct Shape * pointer = (struct Shape *)□
(*pointer).print(); ? ?//?? 将会发生什么???
将得到一个中断错误,结果不是期望的那样。我们期待的是指向Shape的指针对square进行调用
?
现在来看看内存相关,Shape和Square对比
?
Square:
?
width
hight
print
area
? 16B?
?
4 ? ? 4 ? ?4 ? ? 4
?
Shape:
?
print
area
?8B
?
?4 ? ?4
?
print()函数在Shape的内存模型中