我已经编写了一个玩具数据模型,该模型将std::set
用于唯一的实例。可以将其称为flyweight模式或hash-consing(尽管从技术上讲,我还没有使用哈希表)。
#include <set>
#include <cassert>
enum Color { Red, Green, Blue };
struct Shape {
// Set the color, return a new shape.
virtual const Shape* color(Color) const = 0;
};
template<class T>
const T* make_shape(T value) {
struct cmp {
bool operator()(const T* a, const T* b) const {
return *a < *b;
}
};
static std::set<const T*, cmp > values;
auto iter = values.find(&value);
if(iter != values.end()) {
return *iter;
}
auto p = new T(value);
values.insert(p);
return p;
}
struct Circle : public Shape {
Color c = Red;
virtual const Shape* color(Color c) const {
Circle r;
r.c = c;
return make_shape(r);
}
bool operator<(const Circle& rhs) const { return c < rhs.c; }
};
这是我的测试代码。请注意,前两行如何返回相同的指针,因此这些语义与通过new
或make_shared
进行的常规分配不同。
void test_shape() {
auto s0 = make_shape(Circle{});
auto s1 = make_shape(Circle{});
// Structurally equivalent values yield the same pointer.
assert(s0 == s1);
// Color is red by default, so we should get the same pointer.
auto s2 = s0->color(Red);
assert(s2 == s0);
// Changing to Green gives us a different pointer.
auto s3 = s0->color(Green);
assert(s3 != s0);
printf("done\n");
}
int main() {
test_shape();
}
现在,形状已被泄漏。也就是说,一旦该数据模型的客户端不再具有指向Shape
的指针,就不会释放该形状(将set
视为弱引用,应予以破坏)。>
因此,我想使用shared_ptr
来管理我的对象,因为它看起来很简单(也可以接受其他想法,但是我不想添加依赖项,例如boost )。
但是我在shared_ptr
上遇到了一些麻烦。我尝试通过使用std::set
进行比较来更新std::weak_ptr<const T>
来存储owner_before
。
我需要一个shared_ptr
来查找集合中的对象。但这需要更新一个对象,这里的部分重点是能够快速获得一个现有的结构上相等的对象。
更新
我还尝试将set
保留为原始指针,并使用shared_ptr
删除器删除元素。 las,这要求我使用似乎无法接受的shared_from_this
,尽管我不确定为什么:
shape.cpp:30:16: error: member reference base type 'Circle *const' is not a
structure or union
return iter->shared_from_this();
~~~~^ ~~~~~~~~~~~~~~~~
答案 0 :(得分:1)
一个替代解决方案是让您的客户使用拥有其分发的对象的工厂。这样,您的客户可以使用普通指针来引用对象。
一旦客户端完成,它可以与所有对象一起处置工厂。
此外,可能需要对工厂参考进行计数,或者对其保留shared_ptr
。
答案 1 :(得分:0)
这是完整的代码。事实证明,我只是不太擅长编程,并且没有意识到我必须取消对某些内容的引用。
基本方法是shared_ptr
删除程序从set
中删除原始指针。
#include <set>
#include <cassert>
#include <memory>
enum Color { Red, Green, Blue };
struct Shape;
struct Circle;
struct Shape : public std::enable_shared_from_this<Shape> {
virtual ~Shape() = default;
// Set the color, return a new shape.
virtual std::shared_ptr<Shape> color(Color) const = 0;
};
template<class T>
std::shared_ptr<Shape> make_shape(T value) {
struct cmp {
bool operator()(const T* a, const T* b) const {
return *a < *b;
}
};
static std::set<T*, cmp> values;
auto iter = values.find(&value);
if(iter != values.end()) {
return (*iter)->shared_from_this();
}
auto ptr = std::shared_ptr<T>(new T(value), [](T* p) {
printf("deleting %p\n", (void*)p);
values.erase(p); delete p;
});
values.insert(ptr.get());
return ptr;
}
struct CircleCount {
static int count;
CircleCount() { ++count; }
CircleCount(const CircleCount&) { ++count; }
~CircleCount() { --count; }
};
int CircleCount::count;
struct Circle : public Shape {
CircleCount count;
Color c = Red;
virtual std::shared_ptr<Shape> color(Color c) const {
Circle r;
r.c = c;
return make_shape(r);
}
bool operator<(const Circle& rhs) const { return c < rhs.c; }
};
void test_shape() {
{
auto s0 = make_shape(Circle{});
auto s1 = make_shape(Circle{});
assert(s0 == s1);
auto s2 = s0->color(Red);
assert(s2 == s0);
auto s3 = s0->color(Green);
assert(s3 != s0);
}
// All circles should be cleaned up.
printf("circles: %d\n", CircleCount::count);
assert(CircleCount::count == 0);
printf("done\n");
}
int main() {
test_shape();
}
更新
我将其转变为通用名称,可以进行代码审查here