使用显式实例化声明删除后向指针会导致std :: bad_weak_ptr异常 [英] Removing back pointers with the use of explicit instantiation declarations causes std::bad_weak_ptr exception

查看:110
本文介绍了使用显式实例化声明删除后向指针会导致std :: bad_weak_ptr异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开发了一些可以正确编译的代码,但是在(调试)运行时失败。我正在使用VS2015。

I have developed some code that compiles correctly, but fails at (debug) runtime. I am using VS2015.

背景:我正在构建一个高级消息引擎。为了使新消息的编程添加得以维持,在生产代码中,我花了一些时间使用显式初始化声明 C ++结构来编写初始消息。这行之有效,并使新消息的制作变得千篇一律,更不用说将消息传递胆量的维护几乎减少了。以下是此功能的基本代码:

Background: I am building an advanced message engine. To make the programmatic addition of new messages maintainable, in the production code I took the time to craft the initial messages using explicit initialization declaration C++ construct. This works and makes the crafting of new messages cookie-cutter, not to mention reducing the maintenance of the messaging guts to almost nothing. Here is the skeleton code for this functionality:

#include <memory>

template< typename D_T >
struct H // prototype for all explicit initialization declarations (EID)
{
  H( D_T& d ) : x { d } {}
  D_T& x;
};

template< typename D_T >
struct B  // base class for derived objects D1 and D2
{
  B( D_T& d ) : d { d } {}
  D_T& d; // a kind of backptr initialized when the EIDs are contructed

  // actual EIDs a and b
  H< D_T > a { d };
  H< D_T > b { d };
};

struct D1 : public B< D1 >
{
  D1() : B( *this ) {}
  void Func1() {}
};

struct D2 : public B< D2 >
{
  D2() : B( *this ) {}
  void Func2() {}
};



int main()
{
  D1 d1;
  D2 d2;

  // as designed either derived object can access either explicitly initialized member a or b 
  d1.a.x.Func1(); // OK
  d1.b.x.Func1(); // OK
  d2.a.x.Func2(); // OK
  d2.b.x.Func2(); // OK

  return 0;
}

此代码可以编译并运行。

This code compiles and runs.

但是我在真实代码中的派生对象是共享的ptrs。因此,我将此功能添加到了代码中。注意,我正在使用 enable_shared_from_this 构造获取派生类的 this ptr:

But my derived objects in the real code are shared ptrs. I therefore added this functionality to the code. Notice that I am obtaining the derived class’es this ptr using the enable_shared_from_this construct:

#include <memory>

template< typename D_T >
struct H
{
  H( std::shared_ptr< D_T >& d ) : x { d } {}
  std::shared_ptr< D_T >& x;
};

template< typename D_T >
struct B
{
  B( std::shared_ptr< D_T >& d ) : d { d } {}
  std::shared_ptr< D_T >& d;

  H< D_T > a { d }; // a is initialized with D1
  H< D_T > b { d };
};

struct D1: public std::enable_shared_from_this< D1 >, public B< D1 >
{
  D1() : B( shared_from_this() ) {} // runtime error: bad weak prt
  void Func1() {}
};

struct D2: public std::enable_shared_from_this< D2 >, public B< D2 >
{
  D2() : B( shared_from_this() ) {}
  void Func2() {}
};

int main()
{
  D1 d1;
  D2 d2;

  d1.a.x->Func1();
  d1.b.x->Func1();
  d2.a.x->Func2();
  d2.b.x->Func2();

  return 0;
}

此代码会编译。但是,它无法运行,并且在D1构造函数中会以std :: bad_weak_ptr异常中断。

This code compiles. However, it does not run and at the D1 constructor, it breaks with exception std::bad_weak_ptr.

我试图将共享ptrs更改为弱ptrs,但没有成功。有人看到问题了吗?

I have attempted to change shared ptrs to weak ptrs without success. Anyone see the problem?

编辑1:
每个@pat观察到 shared_from_this()不是可从构造函数中调用,请参见下面的修改后的代码,该代码现在可编译并运行:

Edit 1: Per @pat’s observation that shared_from_this() is not callable from the constructor, see the modified code below which now compiles and runs:

#include <memory>

template< typename D_T >
struct H
{
  H( D_T& d ) : x { d } {}
  D_T& x;
};

template< typename D_T >
struct B
{
  B( D_T& d ) : d { d } {}
  D_T& d;

  H< D_T > a { d };
  H< D_T > b { d };
};

struct D1 : public std::enable_shared_from_this< D1 >, public B< D1 >
{
  D1() : B( *this ) {}
  void Func1() {}
};

struct D2 : public std::enable_shared_from_this< D1 >, public B< D2 >
{
  D2() : B( *this ) {}
  void Func2() {}
};

int main()
{
  D1 d1;
  D2 d2;

  d1.a.x.Func1();
  d1.b.x.Func1();
  d2.a.x.Func2();
  d2.b.x.Func2();

  return 0;
}

编辑2:
下面的代码是对我原始帖子的代码,并以@pat的答案为基础。更改内容如下:显式实例化声明(EID)已移至其派生类。 B不再尝试引用派生对象。这是一个明显的错误。作为后向指针的weak_ptr被简单的后向ptr代替(原型中就是这种情况)。由于派生对象(D1和D2)完全拥有对象,因此泄漏没有问题。 (在生产代码中,成员类型是共享的ptrs,以防止泄漏。)

Edit 2: The code below is a re-write of my original posting's code and builds on @pat's answer. Here is what was changed: The explicit instantiation declarations (EID) were moved to their derived classes. B is no longer trying to reference the derived object. This was a plain mistake. The weak_ptr as a back pointer was replaced by a simple back ptr (as was the case in the prototypes). No problem with leaks since the derived objects (D1 and D2) own the object outright. (In the producion code the member types are shared ptrs to prevent leaks.)

#include <memory>
#include <cassert>

template< typename D_T >
struct H
{
  H( D_T* d ) : x { d } {}
  D_T* x;
  int qq { 0 };
};

struct B
{
  B() {}
  int rr { 0 };
};

struct D1 : public B
{
  H< D1 > a { this }; // explicit instantiation declaration 
  int ss { 0 };
};

struct D2 : public B
{
  H< D2 > b { this }; // explicit instantiation declaration
  int tt { 0 };
};


int main()
{
  D1 d1;
  D2 d2;
  d1.rr = 99;
  d2.b.x->rr = 88;
  assert( d1.rr == d1.a.x->rr ); // OK
  assert( d2.rr == d2.b.x->rr ); // OK

  return 0;
}

设计不变,代码维护的复杂度从指数降低(

The design invariant that the code maintenance complexity be reduced from exponential (as was the case in the prototypes) to linear when adding any number of EIDs has been achieved.

推荐答案

该对象必须由一个对象管理共享指针,以使 shared_from_this 起作用。在C ++ 14中,实际上未定义的行为是对尚未由 shared_ptr 管理的对象调用 shared_from_this 。因此,您将无法从构造函数调用 shared_from_this ,因为该对象不会位于 shared_ptr

The object has to be managed by a shared pointer for shared_from_this to work. It's actually undefined behavior in C++14 to call shared_from_this on an object which is not already managed by a shared_ptr. So, you won't be able to call shared_from_this from a constructor since the object won't be inside a shared_ptr at that point.

cppreference中的示例...

Example from cppreference...

struct Good: std::enable_shared_from_this<Good>
{
    std::shared_ptr<Good> getptr() {
        return shared_from_this();
    }
};
// Bad: shared_from_this is called without having std::shared_ptr owning the caller 
try {
    Good not_so_good;
    std::shared_ptr<Good> gp1 = not_so_good.getptr();
} catch(std::bad_weak_ptr& e) {
    // undefined behavior (until C++17) and std::bad_weak_ptr thrown (since C++17)
    std::cout << e.what() << '\n';    
}

这篇关于使用显式实例化声明删除后向指针会导致std :: bad_weak_ptr异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆