铸造模板 class 实例后的虚函数问题
Virtual function problem after casting template class instance
我正在用 C++ 创建一个库,我想随时释放一些对象。使用原始指针我无法通知用户指针不再有效,使用 shared_ptr
如果用户拥有 her/his 拥有的 shared_ptr
我无法释放该对象。所以我决定编写自己的智能指针(ish)class。我的目标是创建一个 class 来计算引用并在引用计数达到 0 时释放内存,这类似于 shared_ptr
,但是它有一个释放内存的 destroy
方法.用户也可以询问内存是否仍然有效(或释放)。
pointer_wrapper
class 包含原始指针和引用计数。正如我之前所说,如果引用计数达到 0 或用户调用 destroy
方法,它会释放原始指针。
template<class T> class pointer_wrapper {
private:
T* raw_pointer;
int32_t reference_count = 1;
public:
pointer_wrapper(T* const raw_pointer): raw_pointer(raw_pointer) { }
T* get_raw_pointer() const { return raw_pointer; }
void increase_reference_count() { reference_count++; }
void decrease_reference_count() {
reference_count--;
if(reference_count == 0) {
delete this;
}
}
int32_t get_reference_count() const { return reference_count; }
void destroy() {
if(raw_pointer != nullptr) {
delete raw_pointer;
raw_pointer = nullptr;
}
}
~pointer_wrapper() { destroy(); }
};
但是 pointer_wrapper
class 仅供内部使用,库的用户将始终获得一个 ptr
实例。用户可以复制 ptr
对象,但所有复制的 ptr
对象的 pw
变量将指向同一个 pointer_wrapper
。这样,如果我调用 ptr
对象之一的 destroy
方法,所有其他 ptr
对象的 is_valid
方法将 return false
。因此,如果库释放了一个对象,如果 she/he 在使用前调用 is_valid
方法,用户将知道这一点。
template<class T> class ptr {
private:
pointer_wrapper<T>* pw;
public:
ptr(T* const raw_pointer) { pw = new pointer_wrapper<T>(raw_pointer); }
ptr(pointer_wrapper<T>* const pw): pw(pw) { pw->increase_reference_count(); }
ptr(const ptr<T>& other_ptr) {
pw = other_ptr.pw;
pw->increase_reference_count();
}
ptr<T>& operator=(const ptr<T>& other_ptr) {
pw->decrease_reference_count();
pw = other_ptr.pw;
pw->increase_reference_count();
return *this;
}
T* operator->() const { return pw->get_raw_pointer(); }
int32_t get_reference_count() const { return pw->get_reference_count(); }
bool is_valid() const { return pw->get_raw_pointer() != nullptr; }
// the problem is probably here
template<class X> ptr<X> convert() const { return ptr<X>(reinterpret_cast<pointer_wrapper<X>*>(pw)); }
void destroy() { pw->destroy(); }
~ptr() { pw->decrease_reference_count(); }
};
一般模式是我有一个类似导出接口的 class,只有纯虚拟方法,我有一个实现 class(未导出,隐藏在 dll 中)继承自类似界面 class.
static ptr<window_system> create() { return ptr<window_system>(new wm_glfw_window_system()); }
这工作正常,直到我尝试通过调用 convert
方法来转换它。如果我在转换后的 ptr
对象上调用方法,我会收到如下错误:
Exception thrown at 0x0000000000000000 in example_1.exe: 0xC0000005: Access violation executing location 0x0000000000000000.
ptr<window_system>(new wm_glfw_window_system())->create_window(...); // works fine
ptr<wm_glfw_window_system>(new wm_glfw_window_system())->create_window(...); // works fine
ptr<wm_glfw_window_system>(new wm_glfw_window_system()).convert<window_system>()->create_window(...); // error
ptr<window_system>(new wm_glfw_window_system()).convert<wm_glfw_window_system>()->create_window(...); // error
所以我想 convert
方法和 reinterpret_cast
有一些问题。但如果我是正确的,我不能使用其他演员表,因为 class ptr<window_system>
和 ptr<wm_glfw_window_system>
不相关,即使 window_system
和 wm_glfw_window_system
class这些是相关的。
所以我的问题是:
- 是否有更好的方法来归档我的目标(例如,具有适当指针类型的库)?
- 我的 class 有意义吗?
- 如何编写我的
convert
方法才能正常工作(它必须支持向下转换)?
不要重复转换。
用指向对象的指针和引用计数助手替换指向包装器的单个指针。
支持在保持相同引用计数的对象指针中进行静态、隐式和动态转换。
(这就是 shared_ptr 的大致工作方式)。
除了您的代码表现出未定义的行为之外,您的问题是指向 base 和 derived 的指针不需要具有相同的值。
with shared_ptr
I couldn't release the object if the user has her/his own shared_ptr
to it.
Is there a better way of archiving my goals (eg. a library with the appropriate pointer type)?
看来您只需要向您的用户提供 std::weak_ptr
。
如果您不希望他们转换和存储 std::shared_ptr
,您可以将其包装在 class 中,例如:
template <class T> class
pointer_wrapper {
private:
std::weak_ptr<T> pointer;
public:
pointer_wrapper(std::weak_ptr<T> pointer): pointer(pointer) {}
template <typename F>
bool do_job(F f) {
if (auto p = pointer.lock()) {
f(*p);
return true;
}
return false;
}
void destroy() {
do_job([](auto& v){
// Inform your library to remove the shared_ptr.
v.release_itself();
});
}
};
我正在用 C++ 创建一个库,我想随时释放一些对象。使用原始指针我无法通知用户指针不再有效,使用 shared_ptr
如果用户拥有 her/his 拥有的 shared_ptr
我无法释放该对象。所以我决定编写自己的智能指针(ish)class。我的目标是创建一个 class 来计算引用并在引用计数达到 0 时释放内存,这类似于 shared_ptr
,但是它有一个释放内存的 destroy
方法.用户也可以询问内存是否仍然有效(或释放)。
pointer_wrapper
class 包含原始指针和引用计数。正如我之前所说,如果引用计数达到 0 或用户调用 destroy
方法,它会释放原始指针。
template<class T> class pointer_wrapper {
private:
T* raw_pointer;
int32_t reference_count = 1;
public:
pointer_wrapper(T* const raw_pointer): raw_pointer(raw_pointer) { }
T* get_raw_pointer() const { return raw_pointer; }
void increase_reference_count() { reference_count++; }
void decrease_reference_count() {
reference_count--;
if(reference_count == 0) {
delete this;
}
}
int32_t get_reference_count() const { return reference_count; }
void destroy() {
if(raw_pointer != nullptr) {
delete raw_pointer;
raw_pointer = nullptr;
}
}
~pointer_wrapper() { destroy(); }
};
但是 pointer_wrapper
class 仅供内部使用,库的用户将始终获得一个 ptr
实例。用户可以复制 ptr
对象,但所有复制的 ptr
对象的 pw
变量将指向同一个 pointer_wrapper
。这样,如果我调用 ptr
对象之一的 destroy
方法,所有其他 ptr
对象的 is_valid
方法将 return false
。因此,如果库释放了一个对象,如果 she/he 在使用前调用 is_valid
方法,用户将知道这一点。
template<class T> class ptr {
private:
pointer_wrapper<T>* pw;
public:
ptr(T* const raw_pointer) { pw = new pointer_wrapper<T>(raw_pointer); }
ptr(pointer_wrapper<T>* const pw): pw(pw) { pw->increase_reference_count(); }
ptr(const ptr<T>& other_ptr) {
pw = other_ptr.pw;
pw->increase_reference_count();
}
ptr<T>& operator=(const ptr<T>& other_ptr) {
pw->decrease_reference_count();
pw = other_ptr.pw;
pw->increase_reference_count();
return *this;
}
T* operator->() const { return pw->get_raw_pointer(); }
int32_t get_reference_count() const { return pw->get_reference_count(); }
bool is_valid() const { return pw->get_raw_pointer() != nullptr; }
// the problem is probably here
template<class X> ptr<X> convert() const { return ptr<X>(reinterpret_cast<pointer_wrapper<X>*>(pw)); }
void destroy() { pw->destroy(); }
~ptr() { pw->decrease_reference_count(); }
};
一般模式是我有一个类似导出接口的 class,只有纯虚拟方法,我有一个实现 class(未导出,隐藏在 dll 中)继承自类似界面 class.
static ptr<window_system> create() { return ptr<window_system>(new wm_glfw_window_system()); }
这工作正常,直到我尝试通过调用 convert
方法来转换它。如果我在转换后的 ptr
对象上调用方法,我会收到如下错误:
Exception thrown at 0x0000000000000000 in example_1.exe: 0xC0000005: Access violation executing location 0x0000000000000000.
ptr<window_system>(new wm_glfw_window_system())->create_window(...); // works fine
ptr<wm_glfw_window_system>(new wm_glfw_window_system())->create_window(...); // works fine
ptr<wm_glfw_window_system>(new wm_glfw_window_system()).convert<window_system>()->create_window(...); // error
ptr<window_system>(new wm_glfw_window_system()).convert<wm_glfw_window_system>()->create_window(...); // error
所以我想 convert
方法和 reinterpret_cast
有一些问题。但如果我是正确的,我不能使用其他演员表,因为 class ptr<window_system>
和 ptr<wm_glfw_window_system>
不相关,即使 window_system
和 wm_glfw_window_system
class这些是相关的。
所以我的问题是:
- 是否有更好的方法来归档我的目标(例如,具有适当指针类型的库)?
- 我的 class 有意义吗?
- 如何编写我的
convert
方法才能正常工作(它必须支持向下转换)?
不要重复转换。
用指向对象的指针和引用计数助手替换指向包装器的单个指针。
支持在保持相同引用计数的对象指针中进行静态、隐式和动态转换。
(这就是 shared_ptr 的大致工作方式)。
除了您的代码表现出未定义的行为之外,您的问题是指向 base 和 derived 的指针不需要具有相同的值。
with
shared_ptr
I couldn't release the object if the user has her/his ownshared_ptr
to it. Is there a better way of archiving my goals (eg. a library with the appropriate pointer type)?
看来您只需要向您的用户提供 std::weak_ptr
。
如果您不希望他们转换和存储 std::shared_ptr
,您可以将其包装在 class 中,例如:
template <class T> class
pointer_wrapper {
private:
std::weak_ptr<T> pointer;
public:
pointer_wrapper(std::weak_ptr<T> pointer): pointer(pointer) {}
template <typename F>
bool do_job(F f) {
if (auto p = pointer.lock()) {
f(*p);
return true;
}
return false;
}
void destroy() {
do_job([](auto& v){
// Inform your library to remove the shared_ptr.
v.release_itself();
});
}
};