我开发了一些可以正确编译但在(调试)运行时失败的代码。我正在使用VS2015。
背景:我正在构建一个高级消息引擎。为了使新消息的编程添加可维护,在生产代码中,我花时间使用explicit initialization declaration
C++ 构造来制作初始消息。这很有效,并且使新消息的制作变得千篇一律,更不用说将消息传递的维护减少到几乎没有。这是此功能的框架代码:
#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;
}
此代码编译并运行。
但是我在真实代码中的派生对象是共享的ptrs。因此,我将此功能添加到代码中。请注意,我正在this
使用以下构造获取派生类的 ptr enable_shared_from_this
:
#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 中断。
我试图将共享 ptrs 更改为弱 ptrs,但没有成功。有人看到问题了吗?
编辑1:根据@pat 的观察,shared_from_this()
构造函数不可调用,请参阅下面的修改后的代码,现在可以编译并运行:
#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 以防止泄漏。)
#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;
}
当添加任意数量的 EID 时,代码维护复杂性从指数(如原型中的情况)降低到线性的设计不变量已经实现。