C与面向对象

本文探讨了栈数据结构的优化过程,从简单的带检查功能的栈开始,逐步分离检查功能,最终实现了一个通用化的校验器栈。通过将特定的检查逻辑封装到独立的Validator结构体中,实现了栈功能的灵活扩展。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一:带检查功能的栈

1.实现

stack.h

#ifndef _STACK_H_
#define _STACK_H_

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

/*栈结构体中加入范围检查*/
typedef struct {
    int top;
    const size_t size;
    int * const pBuf;

    const bool needRangeCheck;
    const int min;
    const int max;
} Stack;

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

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

#define newStackWithRangeCheck(buf, min, max) { \
    0, sizeof(buf) / sizeof(int), (buf),        \
    true, min, max                              \
}

#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;
}

static bool isRangeOk(const Stack *p, int val) {
    return ! p->needRangeCheck ||
        (p->min <= val && val <= p->max);
}

// true: 成功, false: 失敗
bool push(Stack *p, int val) {
    if (! isRangeOk(p, 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;
}

使用:

int buf[16];
Stack stack = newStackWithRangeCheck(buf,0,9);

2.分析:

如果只是这样简单的功能,删除方法足够应对了。但是,上面的代码中仍然存在如下问题:

  • 即使要生成的的不带范围检查功能的栈,栈内也需要保存needRangeCheck、min、max等多余的结构体成员,浪费了内存
  • 如果还想在栈内增加其他校验功能,就必须在结构体内再增加其他成员。这样就必须在结构体内保存所有检查功能的成员,push函数也会因为这些功能变的臃肿,最终导致栈越来越难以应对功能的增加

那么,这次增加的结构体成员needRangeCheck、min、max真的有必要和结构体的其他成员放在相同的位置吗? 这些成员只在isRangeOk函数中被使用,将他们作为结构体成员让所有函数都可以看到是不合适的,也就是说存在作用域污染问题。那么我们需要把这些成员分离出去。

二.将范围检查分离出来的栈

1.实现

stack.h

#ifndef _STACK_H_
#define _STACK_H_

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

/*将范围检查从栈结构体中独立出来*/
typedef struct {
    const int min;
    const int max;
}Range;

typedef struct {
    int top;
    const size_t size;
    int * const pBuf;

    const Range *const pRange;
} Stack;

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

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

#define newStackWithRangeCheck(buf, pRange) { \
    0, sizeof(buf) / sizeof(int), (buf),        \
    pRange                              \
}

#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;
}

static bool isRangeOk(const Range *p, int val) {
    return ! p == NULL ||
        (p->min <= val && val <= p->max);
}

// true: 成功, false: 失敗
bool push(Stack *p, int val) {
    if (! isRangeOk(p->pRange, 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;
}

 

三.通用化校验器的栈

1.实现

stack.h

#ifndef _STACK_H_
#define _STACK_H_

#include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

/*加入校验器,将栈中要检查数据的职责全部转移到Validator结构体中
*第一个成员是函数指针,该函数的参数为指向Validator结构体的指针和需要校验的参数,并返回bool类型
*第二个成员为校验时所需要的数据,由于校验类型不同,所以被校验数据的类型也不尽相同,为了能保存任意*类型的数据,这里使用void指针
*/
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;

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);

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

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

#define rangeValidator(pRange) { \
    validateRange,               \
    pRange                       \
}

#define previousValidator(pPrevious) { \
    validatePrevious,                  \
    pPrevious                          \
}

#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 *pThis, int val) {
    Range *pRange = (Range *)(pThis->pData);
    return pRange->min <= val && val <= pRange->max;
}

bool validatePrevious(Validator *pThis, int val) {
    PreviousValue *pPrevious = (PreviousValue *)pThis->pData;
    if (val < pPrevious->previousValue) return false;
    pPrevious->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;
}
  • 如果使用范围检查,则pData指向Range结构体
  • 如果是push值递增的校验,则pData指针接收的数据为保存上次push的值的结构体
  • 函数指针所指向的校验器函数。Validator的pData成员是void指针,因此先通过类型转换取出校验时所需要的值,然后进行校验

测试:

#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];
    Range range = {0, 9};
    Validator validator = rangeValidator(&range);
    Stack stack = newStackWithValidator(buf, &validator);
    EXPECT_EQ(false, push(&stack, -1));
    EXPECT_EQ(false, push(&stack, 10));
}

TEST(StackTest, pushWithPreviousCheck) {
    int buf[16];
    PreviousValue previous = {0};
    Validator validator = previousValidator(&previous);
    Stack stack = newStackWithValidator(buf, &validator);
    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();
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值