我正在实现一个特殊用途的句柄类。
ihandle
是所有句柄必须实现的接口,在我的实际代码中,->
*
会有运算符重载。但是对于这个例子,我想保持简单,它只有get
函数。
template <typename T>
class ihandle {
public:
virtual T* get();
};
一种可能的实现方式是ptr
,它只是一个原始指针。
template <typename T>
class ptr : public ihandle<T>
{
T* t;
public:
ptr(T* t = nullptr) : t(t) {}
T* get(){return t;}
};
然后有handle
用于进行空安全检查。
template <typename T>
class handle
{
public:
ihandle<T>* h;
T* get(){return h->get();}
handle(ihandle<T>* h = nullptr) : h(h) {}
template <typename D>
handle(handle<D>& hd)
: h((ihandle<T>*)hd.h)
{
static_assert(is_base_of<T, D>::value, "error");
}
};
有一个构造函数可以从继承类的句柄转换为基类的句柄。
template <typename D>
handle(handle<D>& hd)
: h((ihandle<T>*)hd.h)
{
static_assert(is_base_of<T, D>::value, "error");
}
例如,如果B
继承自A
,我希望能够使用handle<B>
的实例调用此函数。
void foo(handle<A> ha) {
// do something
}
但是这为以下样本测试提供了一个段错误。
struct A {
virtual void talk() {printf("A\n");}
};
struct B : public A {
void talk() {printf("B\n");}
};
int main()
{
handle<B> hb(new ptr<B>(new B));
//hb.get()->talk(); // if uncomment, no segfault
handle<A> ha = hb;
ha.get()->talk(); // segfault here
return 0;
}
我怀疑问题可能出在handle(handle<D>& hd)
构造函数中,但我不明白发生了什么。
您可以点击此链接进行测试: https://onlinegdb.com/BkAYuQZ3z
答案 0 :(得分:5)
即使条件is_base_of<T, D>::value
为真,也不会使(ihandle<T>*) hd.h
转换为有效,因为ptr<B>
和ihandle<A>
类型不相关。我想这是一个应该明确避免使用c风格演员的情况的例子。要安全地执行转换,您可以使用dynamic_cast
并使用支票:
: h(dynamic_cast<ihandle<T>*>(hd.h))
{
if(hd.h && !h)
{
throw ::std::runtime_error{"pointers are not related"};
}
}
答案 1 :(得分:1)
handle<B> hb(new ptr<B>(new B));
..但是当我为虚拟方法ihandle.get()提供一些默认实现时,就像这样..
template <typename T>
class ihandle {
public:
virtual T* get() { return NULL; }
};
返回NULL ..我的链接器错误消失了..并且没有崩溃。您测试报告B或B B.
希望我能提供帮助。谢谢你的代码!我正在学习C ++ atm,上面的例子是一个很好的研究。