在创建对象时使用类关系进行适当的封装

时间:2016-10-07 23:56:44

标签: c++ oop

我正在编写一个关于libusb的C ++包装器。我希望我的API完全隐藏底层lib的实现:用户甚至不知道我实际上在使用libusb。所以我创建了两个类:ContextDevice。使用Device方法从Context创建open

所以:

//--- context.hpp -------------------
class Context final
{
public:
  Context();
  ~Context();

  std::unique_ptr<Device> open(uint16_t vid, uint16_t pid);

private:
  struct Impl;
  std::unique_ptr<Impl> impl;
};

//--- context.cpp -------------------
struct Context::Impl
{
  libusb_context* ctx;
};

Context::Context()
  : impl(std::make_unique<Impl>())
{ /* ... */ }

//--- device.hpp -------------------
class Device final
{
public:
  Device();
  ~Device();

private:
  struct Impl;
  std::unique_ptr<Impl> _impl;
};

//--- device.cpp -------------------
struct Device::Impl
{
  libusb_device_handle* handle;
}

Device::Device()
  : _impl(std::make_unique<Impl>())
{}

现在的问题是:如何实施open方法?我需要在实施中调用libusb_open_device_with_vid_pid,该libusb_context*Context::Impl Device::Impl Device,并将句柄存储在Context中。以下是我认为的选项:

  1. 我创建了一个Device的构造函数,它指向libusb_context*,但是Context::Impl的ctor无法访问Device指针来调用该函数; < / LI>
  2. 我按照第一点并将Context放在标题中,让Device成为libusb_context*的朋友:但这听起来很丑陋;
  3. 我创建了一个native_handle的构造函数,它带有一个Context指针:但是我打破了封装,用户不应该看到libusb的详细信息;
  4. 我向open添加了Device方法,因此我可以做第一个并获得实现指针但导致与第三个相同的问题。
  5. 我在libusb_device_handle*中进行了函数调用,但如何使用Device初始化definitions?我不能让anyOf使用这样的指针来打破封装。

1 个答案:

答案 0 :(得分:3)

基于friend的解决方案实际上是最干净的,它是friend的设计目标。

// device.hpp
class Device final
{
public:
  ~Device();

private:
  Device();
  struct Impl;
  std::unique_ptr<Impl> impl;
  friend class Context;
};

// device.cpp
struct Device::Impl
{
  libusb_device_handle* handle;
  Impl() : handle(nullptr) {}
}

Device::Device() : impl(new Device::Impl()) {}

std::unique_ptr<Device> Context::open(uint16_t vid, uint16_t pid) {
  std::unique_ptr<Device> result(new Device());
  result->impl->handle = libusb_open_device_with_vid_pid(vid, pid);
  return result;
}

请注意,您实际上可以返回Device而不是std::unique_ptr<Device>,并将所有绑定视为值对象,以避免额外的间接级别。

编辑:另一种选择是使用void指针。这将删除朋友声明,代价是引入可能不安全的演员。

// device.hpp
class Device final
{
public:
  ~Device();

private:
  Device(void *handle);
  struct Impl;
  std::unique_ptr<Impl> impl;
};

// device.cpp
struct Device::Impl
{
  libusb_device_handle* handle;
  Impl(void *handle)
    : handle(static_cast<libusb_device_handle*>(handle)) {}
}

Device::Device(void *handle)
  : impl(new Device::Impl(handle)) {}

std::unique_ptr<Device> Context::open(uint16_t vid, uint16_t pid) {
  void *handle = libusb_open_device_with_vid_pid(vid, pid);
  std::unique_ptr<Device> result(new Device(handle));
  return result;
}