从构造函数初始化列表中传递此函数

时间:2009-05-15 15:16:07

标签: c++

在以下代码中将'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() {} 
};

如果做到这一点不安全,那么最好的选择是什么?

6 个答案:

答案 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)

如果你只是存放指针供以后使用,它通常是安全的。我做到了这一点。我不会做以下任何事情:

  • 使用它来访问基类或派生类数据(它们可能没有运行它们的构造函数)
  • 做任何多态的,vtable可能不会被初始化。

Here是一篇来自C ++ FAQ的精彩文章,详细介绍了构造函数中“this”的问题。

答案 3 :(得分:3)

在构造函数中启动线程,这些构造函数在仍在构造中的对象中运行代码是危险的。您提供的代码将按原样正常工作,但解决方案很脆弱。

如果DoCallbackA中调用虚拟方法,那么根据线程的运行速度,您可能会得到意外的结果。如果被调用的方法是纯虚拟的,则应用程序将死亡,如果它不是纯粹的,则将调用该方法的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的惯用法非常相似。在这种情况下可能有点矫枉过正,但它没有任何内在错误。