【问题标题】:Using STL containers with classes with private constructors (again)使用带有私有构造函数的类的 STL 容器(再次)
【发布时间】:2016-12-15 01:54:20
【问题描述】:

这是一个类,Unique,用于创建独特的对象。对象是通过工厂方法Unique::Get 创建的,并由传递给此方法的值唯一。该方法检查唯一对象集合Unique::cache中是否存在具有此值(键)的对象,如果没有,则创建一个新对象并插入到集合中。

我希望这个类对用户的使用非常有限。具体来说,用户不应该通过调用Get 方法来复制该类的对象或创建对象。为了实现这一点,我将构造函数和赋值运算符声明为私有的。但是当我这样做时,我不能在类本身中使用 std::set::emplace,因为构造函数是私有的。

可能的解决方案是将std::set 类声明为朋友,但这将允许用户也可以将此容器与我的类一起使用。这可能是有害的。

有没有更好的办法?

#include <set>
#include <iostream>
#include <iomanip>

class Unique {
public:
  // user accessible methods
  static const Unique &Get(int key);
  bool operator==(const Unique &other) const {
    return this == &other;
  }
private:
  // user inaccessible methods
  Unique(int key) : key_(key) {}
  Unique(const Unique &) = delete;
  Unique &operator=(const Unique &) = delete;
  int key_;
private:
  // cache related stuff
  struct Identity {
    Identity(int key) : key_(key) {}
    int key_;
  };
  struct Compare {
    struct is_transparent {};
    bool operator()(const Identity &lhs, const Unique &rhs) const {
      return lhs.key_ < rhs.key_;
    }
    bool operator()(const Unique &lhs, const Identity &rhs) const {
      return lhs.key_ < rhs.key_;
    }
    bool operator()(const Unique &lhs, const Unique &rhs) const {
      return lhs.key_ < rhs.key_;
    }
  };
  static std::set<Unique, Compare> cache;
};

std::set<Unique, Unique::Compare> Unique::cache;

const Unique &Unique::Get(int key) {
  Identity identity(key);
  auto it = cache.find(identity);
  if (it == cache.end()) {
    it = cache.emplace(key).first;
  }
  return *it;
}

int main() {
  const Unique &c1 = Unique::Get(1);
  const Unique &c2 = Unique::Get(1);
  std::cout << std::boolalpha << (c1 == c2) << std::endl;
}

【问题讨论】:

  • 如果你不能复制它们,那么你打算如何从Unique::Get返回的位置获取一个到集合中?可以移动吗?
  • emplace 方法在原地构造元素,它不需要复制构造函数。但它需要一个普通的构造函数才能访问,这就是问题所在。我相信有一些常见的模式可以解决它。
  • 显然,如果你不允许你的类的用户构造实例,你就不允许他们在集合中构造实例,因为那样他们就可以做你正在做的事情试图阻止他们这样做。
  • 所以您似乎希望您的用户能够有时构造实例(特别是当这些实例位于集合中时)。您是否还希望用户能够构建实例?
  • 我的英语远非完美,抱歉。但我想我已经非常清楚地解释了我的意图。我不希望我的类的用户能够以任何直接或间接的方式使用构造函数,除非通过调用工厂方法。并且这个方法应该能够向集合中添加一个元素(它是类的成员)。

标签: c++ stl c++14


【解决方案1】:

有几种常见的技术可以防止直接构造类实例,除非通过“官方渠道”。

这是其中最简单的一个:创建一个必须使用其工厂方法构造的类的示例,但它有一个公共构造函数:

头文件:

class Object {

    class private_inner {};

public:

    Object(const private_inner &);

    static Object factory();
};

你的实现类;

static Object::private_inner inner;

Object Object::factory()
{
    return Object(inner);
}

构造函数需要对私有类成员的引用。即使构造函数是公共的,它也是不可用的,因为私有类成员是不可访问的。

同时,factory() 在构建自身时没有问题。

我相信您可以根据您的类层次结构调整此示例。请注意,您的工厂可以毫无问题地在 C++ 库容器中调用 emplace(),因为您的工厂将拥有对私有类的完全访问权限,并且可以将其传递给容器的方法。

【讨论】:

  • 谢谢你,我可以使用 Identity 类作为我的例子。您能否也分享其他技术(链接被接受:)。
  • pimpl 将是一个。 pimpl 的一种变体是仅公开一个虚拟基类,而实际实现在私有声明的子类中。工厂用new构造子类,并返回指向基类的指针。
猜你喜欢
  • 1970-01-01
  • 2011-08-22
  • 2014-08-22
  • 1970-01-01
  • 1970-01-01
  • 2022-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多