【问题标题】:Using C-like API in OOP在 OOP 中使用类 C API
【发布时间】:2015-01-11 15:54:16
【问题描述】:

我正在用 C++ 编写一些类来充当个人小型库,但遇到了问题。

我的一些对象使我们成为以华丽的 C 风格编写的第三方库。 这意味着这些库具有 apiInit()apiCleanup() 之类的函数,其中前者必须在任何实际 api 函数之前调用,而后者必须在您不再使用它们时调用。

我想要的是为需要库的类提供对其函数的访问点,以确保在创建第一个需要的类时或至少在使用任何 api 函数之前调用 apiInit(),并且 apiCleanup()当使用 api 的最后一个实例被销毁时调用。

请记住,有多个类使用单个库。

我可以提供两种解决方案:

首先,很明显,将提供者设为单例:

#include <iostream>

using namespace std;


class ContextProvider {

  ContextProvider() {
    cout << "Initializing API" << endl;
  }

  ContextProvider(ContextProvider const& rhs) = delete;
  ContextProvider& operator=(ContextProvider const& rhs) = delete;

public:

  ~ContextProvider() {
    cout << "Cleaning API" << endl;
  }

  static ContextProvider& getInstance() {
    static ContextProvider instance;
    return instance;
  }

  void useContext() {
    cout << "Using API" << endl;
  }
};


class ContextUser1 {


public:

  ContextUser1() {

  }

  void doSomething() {
    ContextProvider::getInstance().useContext();
  }
};


class ContextUser2 {

public:

  ContextUser2() {

  }

  void doSomethingElse() {
    ContextProvider::getInstance().useContext();
  }
};

另一种方法是保留上下文用户的计数器,如下所示:

#include <iostream>

using namespace std;


class ContextProvider {
  static unsigned int userCounter;

public:

  ContextProvider() {
    if (userCounter == 0)
      cout << "Initializing API" << endl;

    ++userCounter;
  }

  ~ContextProvider() {
    --userCounter;

    if (userCounter == 0)
      cout << "Cleaning API" << endl;
  }

  void useContext() {
    cout << "Using API" << endl;
  }
};

unsigned int ContextProvider::userCounter = 0;


class ContextUser1 {
  ContextProvider cp;

public:

  ContextUser1() {
    cp = ContextProvider();
  }

  void doSomething() {
    cp.useContext();
  }
};


class ContextUser2 {
  ContextProvider cp;

public:

  ContextUser2() {
    cp = ContextProvider();
  }

  void doSomethingElse() {
    cp.useContext();
  }
};


int main() {
  ContextUser1 cu11, cu12, cu13;
  ContextUser2 cu21, cu22;

  cu11.doSomething();
  cu12.doSomething();
  cu21.doSomethingElse();
  cu22.doSomethingElse();
  cu13.doSomething();
}

两者,当使用以下 main() 执行时

int main() {
  ContextUser1 cu11, cu12, cu13;
  ContextUser2 cu21, cu22;

  cu11.doSomething();
  cu12.doSomething();
  cu21.doSomethingElse();
  cu22.doSomethingElse();
  cu13.doSomething();
}

yield 预期的结果,即:

Initializing API
Using API
Using API
Using API
Using API
Using API
Cleaning API

现在显而易见的问题是,哪个更好,或者我应该使用哪个?

例如,想到的一些事情是……

单例方法:

  • 优点:

    • 无需存储任何计数器。
    • 无需存储任何实例。
  • 缺点:

    • 语法变得奇怪 (ContextProvider::getInstance().use())。
    • 这是一个单例,有所有缺陷。

计数器方法:

  • 优点:

    • 用法很简单。
    • 语法简洁明了 (cp.use())。
  • 缺点:

    • 必须保留用户数量的计数器。
    • 用户类必须存储 ContextProvider 类的实例。

我主要问这个问题是因为我不知道这些优点/缺点中哪一个更重要,如果有我没有考虑到的事情,或者可能有我无法想出的明显的第三种方法天生就比那两个好,或者,谁知道呢。

感谢您的宝贵时间!

【问题讨论】:

  • 您可能正在寻找所谓的"nifty counters"。这是标准库用来确保 std::cout 等在首次使用之前已初始化且可用的技巧。
  • 重新启动 API 的成本是多少? API all 的对象多久会被销毁,然后很快就会创建新的对象?
  • @IgorTandetnik 这不正是我在第二个示例中所做的吗?
  • @rpattiso 是的,我想过这个。我不知道它有多贵,但我正在考虑像 GLFW 或 WinSock 这样的 API,所以这种情况不应该发生,或者至少非常零星发生。
  • 也许code review 更适合这个。

标签: c++ api oop design-patterns singleton


【解决方案1】:

我会使用您的第二种方法,并进行以下修改:

class ContextUser1 {
  std::shared_ptr<ContextProvider> cp;

public:

  ContextUser1(const std::shared_ptr<ContextProvider>& cp)
      : cp(cp) {
  }

  void doSomething() {
      cp->useContext();
  }
};

使依赖项显式化可以使您的代码在可测试性方面更好。此外,使用 shared_ptr 会负责计数,因此您甚至不需要自己执行此操作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-28
    • 1970-01-01
    • 1970-01-01
    • 2011-06-05
    • 1970-01-01
    • 1970-01-01
    • 2014-01-01
    • 2012-07-15
    相关资源
    最近更新 更多