C面向对象(封装 继承 多态)

 

基本思想

面向对象的基本思考方式是的将数据和处理数据的行为放在一起,降低耦合性,其要点就是不要将数据和处理数据的行为分开。

 

一.多态

所谓多态性就是指从调用者的角度看对象,会发现它们非常相似,难以区分,但是这些被调用对象的内部处理实际上各不相同。

结构体声明和调用:

typedef struct _Validator {
    bool (* const validate)(struct _Validator *pThis, int val);
    void * const pData;
} Validator;
bool validate(Validator *p, int val) {
    if (! p) return true;
    return p->validate(p, val);
}

 多态,就是说用同一的接口代码处理不同的数据。比如说,这里的Validator结构就是一个通用的数据结构,我们也不清楚pData是什么数据,validate是什么处理函数?但是,我们处理的时候只要调用p->validate(p, val)就可以了。剩下来的事情我们不需要管,因为不同的接口会有不同的函数去处理,我们只要学会调用就可以了。

 

二.继承

面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类,这使得创建和维护一个应用程序变得更容易。

前面的例子中其实已经对继承有所体现,用pData指针指向Range或PreviousValue结构体;

只不过这种父类子类的继承比较模糊;

typedef struct _Validator {
    bool (* const validate)(struct _Validator *pThis, int val);
    void * const pData;
} Validator;

typedef struct {
    const int min;
    const int max;
} Range;

typedef struct {
    int previousValue;
} PreviousValue;

为了更直观的展示继承关系,使用如下解决方法:

在设计C语言继承性的时候,我们需要做的就是把基础数据放在继承的结构的首位置即可。这样,不管是数据的访问、数据的强转、数据的访问都不会有什么问题。

typedef struct _Validator {
    bool (* const validate)(struct _Validator *pThis, int val);
} Validator;

typedef struct {
    Validator base;
    const int min;
    const int max;
} RangeValidator;

typedef struct {
    Validator base;
    int previousValue;
} PreviousValueValidator;

三.封装

封装是指通过对象的状态和行为集中在一起,并规定其与外部的接口来进行抽象化的过程。

单纯从文字上很难理解,那么把状态认为是结构体中函数指针以外的成员,把行为认为是函数指针成员,就很容易了

封装的主要含义是隐藏内部的行为和信息,使用者只用看到对外提供的接口和公开的信息。

例如:

typedef struct _Validator {
    bool (* const validate)(struct _Validator *pThis, int val);
} Validator;

typedef struct {
    Validator base;
    const int min;
    const int max;
} RangeValidator;
struct _Data;
 
typedef  void (*process)(struct _Data* pData);
 
typedef struct _Data
{
    int value;
    process pProcess;
    
}Data;

 

封装性的意义在于,函数和数据是绑在一起的,数据和结构体是绑在一起的。这样,我们就可以通过简单的一个结构指针访问到所有的数据,遍历所有的函数。封装性,这是类拥有的属性,当然也是数据结构体拥有的属性。

数据隐藏:

1.在C语言中没有提供访问控制功能,无法实现数据隐藏,但这也并非大问题。实际上在前面的例子中,我们使用了const修饰符,这样就不必担心从外部调用不小心修改内部数据了。

2.可以规定成员命名规则来规避这个问题,例如不允许直接访问的成员命名时以下划线“_”开头。

 

完整代码示例:

stack.h

#ifndef _STACK_H_
#define _STACK_H_

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct _Validator {
    bool (* const validate)(struct _Validator *pThis, int val);
} Validator;

typedef struct {
    Validator base;
    const int min;
    const int max;
} RangeValidator;

typedef struct {
    Validator base;
    int previousValue;
} PreviousValueValidator;

typedef struct {
    int top;
    const size_t size;
    int * const pBuf;
    Validator * const pValidator;
} Stack;

bool validateRange(Validator *pThis, int val);
bool validatePrevious(Validator *pThis, int val);

#define newRangeValidator(min, max) \
    {{validateRange}, (min), (max)}

#define newPreviousValueValidator \
    {{validatePrevious}, 0}

bool push(Stack *p, int val);
bool pop(Stack *p, int *pRet);

#define newStack(buf) {                  \
    0, sizeof(buf) / sizeof(int), (buf), \
    NULL                                 \
} 

#define newStackWithValidator(buf, pValidator) { \
    0, sizeof(buf) / sizeof(int), (buf),         \
    pValidator                                   \
}

#ifdef __cplusplus
}
#endif

#endif

 stack.c

#include <stdbool.h>
#include "stack.h"

static bool isStackFull(const Stack *p) {
    return p->top == p->size;
}

static bool isStackEmpty(const Stack *p) {
    return p->top == 0;
}

bool validateRange(Validator *p, int val) {
    RangeValidator *pThis = (RangeValidator *)p;
    return pThis->min <= val && val <= pThis->max;
}

bool validatePrevious(Validator *p, int val) {
    PreviousValueValidator *pThis = (PreviousValueValidator *)p;
    if (val < pThis->previousValue) return false;
    pThis->previousValue = val;
    return true;
}

bool validate(Validator *p, int val) {
    if (! p) return true;
    return p->validate(p, val);
}

// true: 成功, false: 失敗
bool push(Stack *p, int val) {
    if (! validate(p->pValidator, val) || isStackFull(p)) return false;
    p->pBuf[p->top++] = val;
    return true;
}

// true: 成功, false: 失敗
bool pop(Stack *p, int *pRet) {
    if (isStackEmpty(p)) return false;
    *pRet = p->pBuf[--p->top];
    return true;
}

测试代码: 

#include "gtest/gtest.h"

#include <stdbool.h>
#include "stack.h"

TEST(StackTest, popFromEmptyStackReturnsFalse) {
    int buf[16];
    Stack stack = newStack(buf);
    EXPECT_EQ(false, pop(&stack, 0));
}

TEST(StackTest, popReturnsStackTopAndRemoveIt) {
    int buf[16];
    Stack stack = newStack(buf);

    EXPECT_EQ(true, push(&stack, 123));

    int ret;
    EXPECT_EQ(true, pop(&stack, &ret));
    EXPECT_EQ(123, ret);

    EXPECT_EQ(false, pop(&stack, &ret));
}

TEST(StackTest, pushToFullStackReturnsFalse) {
    int buf[16];
    Stack stack = newStack(buf);

    for (int i = 0; i < 16; ++i) push(&stack, i);
    EXPECT_EQ(false, push(&stack, 100));

    int ret;
    EXPECT_EQ(true, pop(&stack, &ret));
    EXPECT_EQ(15, ret);
}

TEST(StackTest, pushWithRangeCheck) {
    int buf[16];
    RangeValidator validator = newRangeValidator(0, 9);
    Stack stack = newStackWithValidator(buf, &validator.base);
    EXPECT_EQ(false, push(&stack, -1));
    EXPECT_EQ(false, push(&stack, 10));
}

TEST(StackTest, pushWithPreviousCheck) {
    int buf[16];
    PreviousValueValidator validator = newPreviousValueValidator;
    Stack stack = newStackWithValidator(buf, &validator.base);
    EXPECT_EQ(true, push(&stack, 3));
    EXPECT_EQ(false, push(&stack, 2));
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值