第九章 高级应用

本章其实是原书第八章后半部分的一些内容,但是这些内容都是一些高级应用,而且目前没有深究的必要,所以单拿出来做一些注释。

指针与线程

  • 线程的内容涉及UNIX系统编程内容,需要了解POSIX线程才能更好的理解该部分,所以这里只是简单的记录了一下互斥锁的使用,完整代码有待学习了《UNIX环境高级编程》中线程相关内容后再进行完善。
  • 以下代码是利用多线程计算向量点积。其中对于和的操作,线程之间会产生冲突,这时便用到了POSIX线程中的互斥锁概念:当某一对象被锁定时,其他对象无法对其进行操作。
typedef struct _vectorInfo
{
    double *vector01;
    double *vector02;
    double sum;
    int length; 
}VectorInfo;

typedef struct _product
{
    vectInfo *vectinfo;
    int beginIndex;
}Product;

首先定义好结构体,其中有几个变量解释一下:

  • vector01vector02是承载实体的向量指针
  • sum点积的结果。就是因为多个线程都要操作此字段,所以才有互斥锁。
  • length指的是每个线程要处理的向量长度。假设向量是由16个元素组成的数组,那么每个线程点积运算时,都可以只运算4个长度,例如线程1计算[0]~[3],线程2计算[4]~[7],以此类推。length字段就是指定的这个长度
  • beginIndexlength配合来确定每个线程计算元素的起始位置。
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的属性。而且函数集合是函数指针,在调用时可以动态的更改到所需要的函数。

C语言模拟多态

接下来创建两个结构体ShapeRect所需要的函数
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++时再详细探讨。

results matching ""

    No results matching ""