【问题标题】:Why does this custom pointer class crash?为什么这个自定义指针类会崩溃?
【发布时间】:2018-04-15 19:39:29
【问题描述】:

我正在实现一个特殊用途的句柄类。

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&lt;B&gt; 的实例调用此函数。

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&lt;D&gt;&amp; hd) 构造函数中,但我不明白发生了什么。

您可以通过单击此链接对其进行测试: https://onlinegdb.com/BkAYuQZ3z

【问题讨论】:

  • 你没有适当的复制构造函数来正常工作handle&lt;A&gt; ha = hb; ... mabbe 规则五和零是你应该查找的东西

标签: c++ pointers smart-pointers


【解决方案1】:

即使条件is_base_of&lt;T, D&gt;::value 为真,它也不会使(ihandle&lt;T&gt;*) hd.h 强制转换有效,因为ptr&lt;B&gt;ihandle&lt;A&gt; 类型不相关。我想这是一个绝对应该避免使用 c 风格转换的例子。为了安全地执行转换,您可以使用dynamic_cast 进行检查:

: h(dynamic_cast<ihandle<T>*>(hd.h))
{
   if(hd.h && !h)
   {
       throw ::std::runtime_error{"pointers are not related"};
   }
}

【讨论】:

  • 我想允许从handle&lt;B&gt;向上转换到handle&lt;A&gt;dynamic_cast也允许从handle&lt;A&gt;向下转换到handle&lt;B&gt;runtime_error 与您提供的代码一起抛出。谢谢!
  • 如果您想让handle&lt;B&gt;handle&lt;A&gt; 的转换工作,那么您只需要存储指向根目标类型的指针(ihandle&lt;A&gt; * h;handle&lt;B&gt;handle&lt;A&gt; 中)或进行类似 COM 的 QueryInterface 方法。
【解决方案2】:

问:您使用的是什么编译器/链接器?当我使用 VS-2017 编译你的东西时,我得到一个链接器错误。它说当我在你的主测试中包含 hb 的声明时,它需要一个 B->get(),

handle<B> hb(new ptr<B>(new B));

..但是当我为虚拟方法 ihandle.get() 提供一些默认实现时,像这样..

template <typename T>
class ihandle {
   public:
   virtual T* get() { return NULL; }
 };

return NULL.. 我的链接器错误消失了.. 并且没有崩溃。您测试报告 B 或 B B。

希望我能提供帮助。感谢您的代码!我正在学习 C++ atm,上面的例子是一个很好的学习..

【讨论】:

  • 有意思,我用的是gcc
猜你喜欢
  • 1970-01-01
  • 2014-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-13
  • 2020-10-30
  • 2017-04-01
相关资源
最近更新 更多