C++API功能设计的实现

2022-11-13 16:11:23 api 功能设计

前言

创建类来表示API中的每个关键对象,同时提供这些类的方法

此处的API风格指的是如何表现API的功能,以下4种:

  • C API

可以用C编译器编译的API。这种API只包含一组自由函数以及辅助的数据结构和常量。这种风格的接口不包含对象或继承,因此被称为纯C模式

  • 面向对象的C++ API

这种风格涉及对象(其中包含相关的数据与方法)的使用以及继承、封装和多态等概念的应用

  • 基于模板的API

通过模板功能,C++也支持泛型编程和元编程。它支持以泛型类型的方式编写函数和数据结构,在以后使用时,泛型类型可以通过具体类型来实例化,从而实现特化

  • 数据驱动型API

这类接口的特点是,将参数通过灵活的数据结构打包,连同命名的命令一起发送给处理程序,而不是调用特定的方法和自由函数

纯C API

C语言不支持对象封装和继承层次结构等概念,因此,纯C语法的API必须使用一组更为受限的语言特性来表示,比如typedef、结构体和全局命名空间中的函数调用等。因为C语言中没有namespace关键字,要避免与其他C库中的名字发生冲突,对这种风格的API而言,所有公开的函数和数据结构应该使用一个公共的前缀

当然,也可以使用内部链接隐藏实现中的符号名,比如将符号名声明为静态的,这样它们的作用域就限制在.c文件之中了。通过这种方式,可以确保任何这样的函数都不会被导出到外部,从而不会导致符号冲突

// c++ 示例
class Stack
{
public:
    void Push(int val);
    int Pop();
    bool IsEmpty() const;
private:
    int *mStack;
    int mCurSize;
};
// 纯C API
struct Stack
{
    int *mStack;
    int mCurSize;
};
void StackPush(struct Stack *stack, int val);
int StackPop(struct Stack *stack);
bool StackIsEmpty(const struct Stack *stack);
// 进一步改进
typedef struct Stack *StackPtr;
void StackPush(StackPtr stack, int val);
int StackPop(StackPtr stack);
bool StackIsEmpty(const StackPtr stack);
// 可以通过特定的API调用来完成数据库结构的创建与销毁
StackPtr StackCreate();
void StackDestory(StackPtr stack);

C API的头文件中使用extern "C"限制,以便C++程序能够正确的编译和链接C API

#ifdef _cplusplus
extern "C" {
#endif
// C API声明
#ifdef _cplusplus
}
#endif

面向对象的C++ API

过程式编程、泛型编程、函数式编程

使用面向对象的C++概念创建二进制兼容的API是极为困难的

基于模板的API

模板可以用来编写在编译时生成代码或执行代码的程序(该技术称为元编程)

模板可以在编译时执行一些工作,进而改进运行时性能

#include <vector>
template <typename T>
class Stack
{
public:
    void Push(T val);
    T Pop();
    bool IsEmpty() const;
private:
    std::vector<T> mStack;
};
// 可以定义一个typedef,这样就可以更方便地使用该模板实例了
typedef Stack<int> IntStack;
IntStack *stack = new IntStack();

模板实现方式的另一个选择是,利用C预处理器来定义一段文本,可以将其放入多个头文件中

#include <vector>
#define DECLARE_STACK(Prefix, T) \
class Prefix##Stack \
{ \
public: \
    void Push(T val); \
    T Pop(); \
    bool IsEmpty() const; \
    
private: \
    std::vector<T> mStack; \
}
DECLARE_STACK(Int, int);

模板提供了一种类型安全的在编译时生成代码的方式。你可以调试到类模板的时机代码行中。除非你要编写纯C API,无法使用模板,否则就应该避免使用预处理器来模拟模板

模板的一个重要属性是,不同于使用继承时的动态(运行时)多态,它支持静态(编译时)多态

不会像虚方法那样存在运行时代价

模板进一步的益处,对于特定类型的实例类,可以特化它的某些方法

template <>
void Stack<int>::Push(int val)
{
    // 实现特定于int类型的压栈功能
}

基于模板的API的缺点

  • 最严重的问题是:类模板的定义通常必须出现在公开的头文件中
  • 因为要特化模板,编译器必须能够访问模板代码的完整定义,显而易见,这会暴露内部细节
  • 每当其他文件包含了类模板定义所在的头文件时,内联的代码都需要重新编译,生成的代码会被添加到每个使用该API的模块的目标文件。这会增加编译时间,并致使代码膨胀
  • 实际上有些情况下你可以使用显式实例化技术将模板的实现隐藏在.cpp文件中
  • 模板的另一个缺点是,模板代码中出现错误时,大多数编译器生成的报错信息都是冗长且令人困惑的,可用STLFilt

相对于运行时开销而言,代码体积是需要优先考虑的因素,那么应该选择面向对象方案,而非模板。或者相反,如果运行时性能更为重要,那就应该选择模板

数据驱动型API

数据驱动型程序指的是:通过每次运行时提供不同的输入数据,它可以执行不同的操作

优点

  • 对于API将来可能发生的变化,它的容错性更强
  • 可以更容易地支持数据驱动型测试技术p143

API支持可变参数列表

  • 联合体
  • 继承
  • void *

到此这篇关于C++ API功能设计的实现的文章就介绍到这了,更多相关C++ API内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章