【问题标题】:C++ static list with private constructor带有私有构造函数的 C++ 静态列表
【发布时间】:2014-10-12 00:29:52
【问题描述】:

如果有人问过这个问题,我深表歉意,我不确定如何用最好的词来表达,也找不到。

我基本上有一个类,我想维护一个自身的映射,并且该列表应该具有该对象的唯一实例。

using std::unordered_map;
class MyClass
{
    ~MyClass() {};
    MyClass() {};   // these actually contain code which operate on the classes data
    static unordered_map<Uint32, MyClass> list;
public:
    static const MyClass& GetObject(Uint32 key) {return list[key];};

};

当我编译我的代码时,它基本上给了我一堆来自 STL 的错误,说它正在调用已删除的函数等,这是有道理的,因为 unordered_map 可能使用构造函数和析构函数,所以我将 unordered_map 声明为朋友

friend class unordered_map<Uint32, MyClass>;

但是似乎没有更少的错误,我推测这是由于 unordered_map 使用的类,如pair和hash。所以我的问题是是否有替代方案。我应该只声明更多似乎在编译器中出错的朋友,还是有其他方法?

【问题讨论】:

  • "使用 std::unordered_map"
  • 哦,打错字了,抱歉,你的意思是朋友。
  • 你在哪里初始化你的静态成员变量?
  • 程序另一部分的翻译单元,但是这将是一个链接错误,这是一个编译错误。

标签: c++ static friend access-control private-constructor


【解决方案1】:

所以。你有心情做一些烦人的事。所以让我们去做吧。正如 AlexD 所说,您缺少的是公共析构函数。 unordered_map 需要访问它(可能通过一些实现定义的内部类)。

所以让我们这样做,让我们做你应该做的事情,那就是制作一个更小更简单的测试用例:

#include <unordered_map>

class MyClass {
    public:
        ~MyClass() {}

    private:
        MyClass() {}
};

int main() {
    std::unordered_map<int, MyClass> x;
    x.at(3);
    //x[3];
}

Now that compiles fine.

现在,请注意我已经注释掉了x[3]。我们不能用那个。这是因为如果地图中不存在 3,我们将调用 MyClass 的默认构造函数,它是私有的。而且由于编译器在编译时不知道这是否属实,所以它需要确保它可以调用构造函数。


cmets 担心您无法在此地图中插入任何对象。好吧,让我们添加一个静态工厂方法并摆脱这个问题:

#include <unordered_map>
using std::unordered_map;

class MyClass {
    public:
        static MyClass factory() { return MyClass(); }
        ~MyClass() {}

    private:
        MyClass() {}
        int x;
};

int main() {
    std::unordered_map<int, MyClass> x;
    x.insert(std::make_pair(3, MyClass::factory()));
    x.emplace(4, MyClass::factory());
}

【讨论】:

  • 啊哈,但看到我想要这种行为:我希望 unordered_map 调用默认构造函数,如我所示。否则我应该如何将数据放在地图中。
  • 你会使用插入函数:std::unordered_map::insert
  • 那我需要一个拷贝构造函数吗?
  • 我已经更新了我的答案。还有一个隐式的公共复制构造函数。 link。此外,如果我们使用~MyClass() = default;,我们也会得到一个隐式移动构造函数。
  • 您并没有真正在问题的约束范围内进行操作。关键是我不希望任何其他类能够构造 MyClass,但是静态工厂函数和移动构造函数都允许函数超出 MyClass 的范围来构造 MyClass。我的解决方案是用我在 MyClass 中定义的静态函数替换 operator[]。这有点烦人,但我会处理的。
【解决方案2】:

更新:T.C.请指出我忽略的几件事,所以这个答案完全改变了......

#include <iostream>
#include <unordered_map>
#include <map>
#include <cinttypes>

class MyClass
{
    typedef std::unordered_map<uint32_t, MyClass> Instances;
    friend Instances;
    friend std::pair<uint32_t, MyClass>;
    friend std::pair<const uint32_t, MyClass>;
  public:
    static const MyClass& getObject(uint32_t key) { return instances_[key] = 2 * key; }
    ~MyClass() {}
    int n() const { return n_; }
  private:
    MyClass() : n_(-1) { }
    MyClass& operator=(int n) { n_ = n; return *this; }
    int n_;
    static Instances instances_;
};

MyClass::Instances MyClass::instances_;

int main() {
    const MyClass& m20 = MyClass::getObject(20);
    const MyClass& m21 = MyClass::getObject(21);
    std::cout << m20.n() << ' ' << m21.n() << '\n';
}

以上代码at ideone.com

根据 cmets,标准没有记录必要的友谊列表,因此可能会因新编译器版本或移植到另一个编译器而中断。

或者,您可以将(智能)指针存储在unordered_map 中。

【讨论】:

  • 哦,所以 std::map 是明确允许的?
  • @bathtub:没有这样的运气 - 上面已经澄清了。 Boost 提供了一个产品: boost::container::map - 我已经阅读了支持的不完整类型,但在官方文档中没有 - 尚未找到任何确认。
  • 只是为了在未来的问题中清晰的提示 - 最好将它们称为容器而不是列表,这会唤起 std::list - 双向链表数据结构。 std::shared_ptrstd::unique_ptr 绝对适用于此...参数不需要完整。不过,只有 std::arraystd::string 的可选短字符串优化可能会将东西放在堆栈上 - vectormapunordered_maplist 等都使用 new 分配。
  • 其实我觉得类型的不完整性不是问题。类内声明static unordered_map&lt;Uint32, MyClass&gt; list; 实际上并没有实例化模板(因为它是static)。命名空间范围的稍后定义是实例化模板的内容,但此时该类已经是一个完整的类型。
  • @TonyD 9.2/p2 "在类成员规范中,类在函数体中被认为是完整的" :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-28
  • 1970-01-01
相关资源
最近更新 更多