第九章 高级应用
本章其实是原书第八章后半部分的一些内容,但是这些内容都是一些高级应用,而且目前没有深究的必要,所以单拿出来做一些注释。
指针与线程
- 线程的内容涉及UNIX系统编程内容,需要了解POSIX线程才能更好的理解该部分,所以这里只是简单的记录了一下
互斥锁
的使用,完整代码有待学习了《UNIX环境高级编程》中线程相关内容后再进行完善。 - 以下代码是利用多线程计算向量点积。其中对于和的操作,线程之间会产生冲突,这时便用到了POSIX线程中的
互斥锁
概念:当某一对象被锁定时,其他对象无法对其进行操作。
typedef struct _vectorInfo
{
double *vector01;
double *vector02;
double sum;
int length;
}VectorInfo;
typedef struct _product
{
vectInfo *vectinfo;
int beginIndex;
}Product;
首先定义好结构体,其中有几个变量解释一下:
vector01
和vector02
是承载实体的向量指针sum
点积的结果。就是因为多个线程都要操作此字段,所以才有互斥锁。length
指的是每个线程要处理的向量长度。假设向量是由16个元素组成的数组,那么每个线程点积运算时,都可以只运算4个长度,例如线程1计算[0]~[3]
,线程2计算[4]~[7]
,以此类推。length
字段就是指定的这个长度beginIndex
跟length
配合来确定每个线程计算元素的起始位置。
pthread_mutex_t sumLock;
void dotProduct(void *prd)
{
int i;
Product *product = (Product *)prd;
VectorInfo *vectorinfo = product->vectinfo;
int beginIndex = product->beginIndex;
int endIndex = beginIndex + product->length;
double total = 0;
for (i = beginIndex; i < endIndex; ++i)
total+=vectorinfo->vector01[i] * vectorinfo->vector02[i];
pthread_mutex_lock(&sumLock);
vectorinfo->sum+=total;
pthread_mutex_unlock(&sumLock);
pthread_exit((void *)0);
}
这就是进行点积运算的核心,其中有几个变量解释一下:
pthread_mutex_t sumLock
是POSIX线程的内容,通过pthread_mutex_t
定义了一个互斥锁变量- 通过循环,每个线程处理向量的一段,进行点积运算,然后将结果加到总和
SUM
上 - 锁定互斥锁,开始操作
sum
变量,然后解开锁,这样每个线程都可以不产生冲突的操作sum
变量了。
指针与回调函数
由于本人目前对回调函数还不是很了解,所以这里暂时略过,等用到时再来学习
指针与面向对象技术 - 隐式指针
本章内容与数据结构结合比较密切,所以放到学习数据结构时进行补充
指针与面向对象技术 - 多态
多态的技术主要是运用了函数指针。正如之前在函数与指针那一章所提到的,函数指针正是实现面向对象技术多态的关键。通过定义函数指针,在建立结构体时,动态的指向不同函数而实现多态方式。具体代码可以参看 ploy.c 这里会对代码进行说明
首先创建结构体,这里的结构体就是模拟类的行为
typedef void (*fptrSet)(void*, int);
typedef int (*fptrGet)(void*);
typedef void (*fptrDisplay)();
typedef struct _vfunc
{
fptrSet setX;
fptrGet getX;
fptrSet setY;
fptrGet getY;
fptrDisplay display;
} vFunc;
typedef struct _shape
{
vFunc function;
int x;
int y;
} Shape;
typedef struct _rect
{
Shape base;
int w;
int h;
} Rect;
- 首先是函数集合结构体
vFunc
。这里定义的函数都是类似于面向对象属性的读写操作。这里将使用函数指针。 Shape
就是某种意义上的根类。其内部定义了两个私有变量,然后通过引用函数集合结构体,算作是类的方法Rect
结构体可以等同于Shape
的派生类,其内部有两个私有变量,然后就是包含了整个Shape
结构体。这样Rect
也有了Shape
的属性。而且函数集合是函数指针,在调用时可以动态的更改到所需要的函数。
接下来创建两个结构体Shape
和Rect
所需要的函数
void displayShape(){printf("Shape\n");}
void ShapeSetX(Shape *shape, int x){shape->x = x;}
void ShapeSetY(Shape *shape, int y){shape->y = y;}
int ShapeGetX(Shape *shape){return shape->x;}
int ShapeGetY(Shape *shape){return shape->y;}
void displayRect(){printf("Rect\n");}
void RectSetW(Rect *rect, int x){rect->w = x;}
void RectSetH(Rect *rect, int y){rect->h = y;}
int RectGetW(Rect *rect){return rect->w;}
int RectGetH(Rect *rect){return rect->h;}
创建建立实例的函数
Shape *newShape()
{
Shape *shape = (Shape *)malloc(sizeof(Shape));
shape->x = 10;
shape->y = 10;
shape->function.setX = ShapeSetX;
shape->function.getX = ShapeGetX;
shape->function.setY = ShapeSetY;
shape->function.getY = ShapeGetY;
shape->function.display = displayShape;
return shape;
}
Rect *newRect()
{
Rect *rect = (Rect *)malloc(sizeof(Rect));
rect->w = 20;
rect->h = 30;
rect->base.function.setX = RectSetW;
rect->base.function.getX = RectGetW;
rect->base.function.setY = RectSetH;
rect->base.function.getY = RectGetH;
rect->base.function.display = displayRect;
return rect;
}
正如刚才所说,这里通过函数集合结构体,分别指向需要的函数原型,从而实现了多态的效果。这两个建立实例的函数其实就很像C++中的构造函数了。那么既然有构造函数,就不要忘了在最后要释放内存,C语言是没有自动析构功能的!
这里要注意,由于函数指针fptrSet和fptrGet内部参数为void *。而直接调用时传入的是Shape *和Rect *。这里会导致编译器发出警告,至于怎么解决,目前还没有方法。需要以后深入研究。
2016年6月15日更新 我将这个问题放到了stackoverflow上,最终得到了解答,更新后的代码如下。这样就不会产生警告了。
void displayShape(){printf("Shape\n");}
void ShapeSetX(void *voidShape, int x){Shape *shape = (Shape*)voidShape;shape->x = x;}
void ShapeSetY(void *voidShape, int y){Shape *shape = (Shape*)voidShape;shape->y = y;}
int ShapeGetX(void *voidShape){Shape *shape = (Shape*)voidShape;return shape->x;}
int ShapeGetY(void *voidShape){Shape *shape = (Shape*)voidShape;return shape->y;}
void displayRect(){printf("Rect\n");}
void RectSetW(void *voidRect, int x){Rect *rect = (Rect *)voidRect;rect->w = x;}
void RectSetH(void *voidRect, int y){Rect *rect = (Rect *)voidRect;rect->h = y;}
int RectGetW(void *voidRect){Rect *rect = (Rect *)voidRect;return rect->w;}
int RectGetH(void *voidRect){Rect *rect = (Rect *)voidRect;return rect->h;}
然后就是在主函数中使用之前定义的函数了。
int i;
Shape *sptr[2];
sptr[0] = newShape();
sptr[1] = newShape();
sptr[0]->function.setX(sptr[0],111);
sptr[1]->function.setX(sptr[1],112);
for (i = 0; i < 2; ++i)
{
sptr[i]->function.display();
printf("(%d, %d)\n", sptr[i]->function.getX(sptr[i]), sptr[i]->function.getY(sptr[i]));
}
Rect *rect = newRect();
rect->base.function.display();
printf("(%d, %d)\n", rect->base.function.getX(rect), rect->base.function.getY(rect));
free(sptr[0]);sptr[0]=NULL;
free(sptr[1]);sptr[1]=NULL;
free(rect);rect=NULL;
最后不要忘了释放内存!
以上的例子简单的实现了利用C语言中的指针实现多态技术。在C++中,实现多态的方式是虚表(VTable)
。作用原理跟vFunc
很相似。待到以后深入学习C++时再详细探讨。