在以下代码中将'this'传递给初始化列表中的另一个对象时是否有任何问题?
class Callback { public: virtual void DoCallback() = 0; };
class B
{
Callback& cb;
public:
B(Callback& callback) : cb(callback) {}
void StartThread();
static void Thread()
{
while (!Shutdown())
{
WaitForSomething();
cb.DoCallback();
}
}
};
class A : public Callback
{
B b;
public:
A() : b(*this) {b.StartThread();}
void DoCallback() {}
};
如果做到这一点不安全,那么最好的选择是什么?
答案 0 :(得分:10)
如果您非常小心,这将正常工作。如果您开始调用虚方法或使用依赖于该类型中其他对象的方法,您将遇到很多麻烦。但是,如果你只是设置一个参考,这应该工作正常。
更安全(但不是完全安全)的替代方法是在构造函数完成后稍后再设置b。这不会消除vtable问题,但会消除在构造之前访问其他成员变量等问题。
class A : public Callback {
std::auto_ptr<B> spB;
public:
A() {
spB.reset(new B(this));
spB->StartThread();
}
};
答案 1 :(得分:6)
如果另一个类只存储指针/引用,就像你的情况一样,它是安全的。但是你必须确保传递this
的构造函数/函数在A
的构造函数完成之前没有尝试访问引用的对象。 A
对象尚未完全构造,调用A
方法并访问属性可能会导致未定义的结果。
答案 2 :(得分:4)
如果你只是存放指针供以后使用,它通常是安全的。我做到了这一点。我不会做以下任何事情:
Here是一篇来自C ++ FAQ的精彩文章,详细介绍了构造函数中“this”的问题。
答案 3 :(得分:3)
在构造函数中启动线程,这些构造函数在仍在构造中的对象中运行代码是危险的。您提供的代码将按原样正常工作,但解决方案很脆弱。
如果DoCallback
在A
中调用虚拟方法,那么根据线程的运行速度,您可能会得到意外的结果。如果被调用的方法是纯虚拟的,则应用程序将死亡,如果它不是纯粹的,则将调用该方法的A
版本而不是派生版本。这与你永远不应该从构造函数中调用虚方法完全相同。
更安全的方法是让用户调用线程。这也是boost :: thread库和即将推出的标准中的方法。创建并初始化将要执行的对象,然后将其传递给正在运行的线程:
class Worker
{
public:
void DoWork();
};
void startWorkerThread()
{
Worker w; // fully create the object that is going to be run before you...
boost::thread thr( boost::bind( &Worker::DoWork, &w ) ); // ...create thread and run
}
答案 4 :(得分:2)
如果您记住this
对象尚未完全构建,那么它是安全的。如果你的B
类只存储指针,而不在构造函数中调用它上面的任何函数,那么你就是安全的。如果您尝试从B的构造函数访问指针,则必须非常小心并注意A的成员初始化的顺序。
答案 5 :(得分:0)
只要确保引用仍然有效,这与称为double dispatch的惯用法非常相似。在这种情况下可能有点矫枉过正,但它没有任何内在错误。