提问人:Niklas 提问时间:9/7/2022 最后编辑:Niklas 更新时间:9/8/2022 访问量:164
如何围绕引用计数类型实现可复制和可移动的包装器?
How to implement copyable and movable wrapper around reference counted type?
问:
假设 C API 提供了一个具有内部引用计数的不透明结构:
struct Opaque {
int data;
int refcount;
};
struct Opaque* opaque_new(int data) {
return new Opaque {
.data = data,
.refcount = 1
};
}
int opaque_data(struct Opaque* opaque) {
return opaque->data;
}
struct Opaque* opaque_ref(struct Opaque* opaque) {
opaque->refcount++;
return opaque;
}
void opaque_unref(struct Opaque* opaque) {
opaque->refcount--;
if (!opaque->refcount) {
delete opaque;
}
}
如何创建可复制、可移动、可复制可分配和可移动可分配的包装器类型?
到目前为止,我有:
#include <algorithm>
class Wrapper {
public:
Wrapper() : m_opaque(nullptr) {}
explicit Wrapper(int data) : m_opaque(opaque_new(data)) {}
Wrapper(const Wrapper&) = delete; // TODO
Wrapper(Wrapper&& wrapper) : m_opaque(wrapper.m_opaque) {
wrapper.m_opaque = nullptr;
}
Wrapper& operator=(const Wrapper&) = delete; // TODO
Wrapper& operator=(Wrapper&& other) {
swap(other);
return *this;
}
~Wrapper() {
if (m_opaque) {
opaque_unref(m_opaque);
}
}
void swap(Wrapper& other) {
std::swap(m_opaque, other.m_opaque);
}
int getData() const {
if (m_opaque) {
return opaque_data(m_opaque);
} else {
return 0;
}
}
private:
struct Opaque* m_opaque;
};
我特别不希望在引用计数之上进行引用计数,而是与自定义删除器一起使用。std::shared_ptr
实现其余方法(特别是复制分配)的简明方法是什么?有没有更好的方法来实现我目前得到的那些?
答:
0赞
oraqlle
9/7/2022
#1
我假设您想要 Wrapper 对象的离散副本,因此您想要指向新数据而不是相同的数据(因此使用 copy ctor/assignment)。在这种情况下,您只需创建一个指向不透明类型的新指针,从传递给 ctor/assignment-operator 的不透明类型中复制值。m_opaque
注意1:对于复制分配,我添加了一个if,用于检查传递的不是“this”(即)。如果是这样,它只会自行返回而不会产生副作用。wrap
Wrapper
w1 = w1
注意2:我还添加了一种方法,使它看起来更简洁。您可以将其设为私有/公共或其他任何内容,也可以直接使用 .getRefCount()
->
例如。
class Wrapper
{
/// Other constructors ...
Wrapper(const Wrapper wrap)
{
m_opaque = new Opaque {
.data = wrap.getData(),
.refcount = wrap.getRefCount()
};
}
Wrapper& operator=(const Wrapper wrap)
{
if (*this != wrap)
{
m_opaque = new Opaque {
.data = wrap.getData(),
.refcount = wrap.getRefCount()
};
}
return *this;
}
int getRefCount()
{ return m_opaque->refcount; }
/// Other methods ...
};
评论
1赞
gerum
9/8/2022
我假设他不想要这个,因为如果你想复制真实数据,也使用 Wrapper = Opaque 也可以。
0赞
oraqlle
9/8/2022
这是真的,但从我从问题中得到的是,即。复制 CTOR 用于制作对象的副本。 不是一个复制CTOR,而是一种显式的构造函数。how to make a copy ctor for a wrapper type
Wrapper
Wrapper
Wrapper(const Opaque& oq)
0赞
gerum
9/8/2022
你如何获得那个转换构造函数,没有人谈论这样的事情。另一种解释是,模仿shared_ptr的行为,它有一个复制者,但它不复制数据。但最终的答案只能来自OP。
3赞
Artyer
9/8/2022
#2
Boost 的intrusive_ptr
是针对“内部参考计数”而制定的:
#include <boost/intrusive_ptr.hpp>
// intrusive_ptr API functions
inline void intrusive_ptr_add_ref(Opaque* opaque) noexcept {
::opaque_ref(opaque);
}
inline void intrusive_ptr_release(Opaque* opaque) noexcept {
::opaque_unref(opaque);
}
struct Wrapper {
public:
// Boost.intrusive_ptr operators are fine
Wrapper() noexcept = default;
Wrapper(const Wrapper&) noexcept = default;
Wrapper(Wrapper&&) noexcept = default;
Wrapper& operator=(const Wrapper&) noexcept = default;
Wrapper& operator=(Wrapper&&) noexcept = default;
// (false means don't add a refcount, since opaque_new does that)
explicit Wrapper(int data) : m_opaque(opaque_new(data), false) {
// if (!m_opaque) throw std::bad_alloc();
}
void swap(Wrapper& other) noexcept {
m_opaque.swap(other.m_opaque);
}
friend void swap(Wrapper& l, Wrapper& r) noexcept {
l.swap(r);
}
int getData() const {
if (m_opaque) {
return ::opaque_data(m_opaque.get());
} else {
return 0;
}
}
private:
boost::intrusive_ptr<Opaque> m_opaque;
};
https://godbolt.org/z/oGEdd66Yo
你也可以用“复制和交换”的习惯法来实现这一点:
Wrapper(Wrapper&& other) noexcept : m_opaque(std::exchange(other.m_opaque, nullptr)) {}
Wrapper(const Wrapper& other) : m_opaque(other.m_opaque) {
if (m_opaque) opaque_ref(m_opaque);
}
// Also works for move assign
Wrapper& operator=(Wrapper copy) noexcept {
this->swap(copy);
return *this;
}
不相关,但 C 函数不应引发 C++ 异常(失败时)。您可以使用它返回而不是引发异常。或者你可以让它中止而不是扔掉。
在函数内部进行检查真的会有什么坏处吗?opaque_new
new
new (std::nothrow) Wrapper { ... }
nullptr
nullptr
评论