这个指向基类构造函数的指针?

时间:2010-03-29 14:11:36

标签: c++ class-design

我想实现一个派生类,它也应该实现一个接口,它有一个基类可以调用的函数。下面给出了一个警告,因为将this指针传递给基类构造函数是不安全的:

struct IInterface
{
    void FuncToCall() = 0;
};

struct Base
{
    Base(IInterface* inter) { m_inter = inter; }

    void SomeFunc() { inter->FuncToCall(); }

    IInterface* m_inter;
};

struct Derived : Base, IInterface
{
    Derived() : Base(this) {}

    FuncToCall() {}
};

最好的方法是什么?我需要提供接口作为基础构造函数的参数,因为它并不总是作为接口的衍生类;有时它可能是一个完全不同的类。

我可以在基类SetInterface(IInterface * inter)中添加一个函数,但我想避免这种情况。

6 个答案:

答案 0 :(得分:4)

您不会从构造函数中发布this,因为此时您的对象尚未正确初始化。但是,在这种实际情况下,它似乎是安全的,因为您只将它发布到基类,它只存储它,并且直到稍后某个时候才会调用它,到那时构造将完成。

但是,如果要删除警告,可以使用静态工厂方法:

struct Base
{
public:
    Base() { }

    void setInterface(IInterface* inter) { m_inter = inter; }

    void SomeFunc() { inter->FuncToCall(); }

    IInterface* m_inter;
};

struct Derived : Base, IInterface
{
private:
    Derived() : Base() {}

public:
    static Derived* createInstance() {
        Derived instance = new Derived();
        instance->setInterface(instance);
        return instance;
    }

    FuncToCall() {}
};

请注意Derived的构造函数是私有的,以确保仅通过createInstance进行实例化。

答案 1 :(得分:3)

您始终可以推迟界面取消引用:

struct IInterface
{
    virtual ~IInterface();
    virtual void FuncToCall() =0;
};

class Base { public: virtual ~Base(); void SomeFunc() { GetInterface().FuncToCall(); } private: virtual IInterface& GetInterface() =0; };

class Derived: public Base, public IInterface { public: private: virtual IInterface& GetInterface() { return *this; } virtual void FuncToCall(); };

答案 2 :(得分:2)

您可以执行一组有限的操作(由标准保证),并指向尚未初始化的对象,并将其存储以供进一步使用。编译器可能会发出警告,因为很容易误用Base中的接收指针。

请注意,除了存储之外,指针的大多数用法都是未定义的行为,但上面的代码是正确的。

答案 3 :(得分:1)

怎么样:

struct IInterface
{
    void FuncToCall() = 0;
    IInterface* me() { return this; }
};

...

struct Derived : IInterface, Base
{
    Derived() : IInterface(), Base(me()) {}

    FuncToCall() {}
};

在我们到达Base构造函数接口的时候已经初始化了,所以对me()的调用是安全的,并且没有警告

答案 4 :(得分:0)

如何将protected:用于IInterface* m_inter;

struct IInterface
{
    virtual void FuncToCall() = 0;
};

struct Base
{
    Base(IInterface* inter) { m_inter = inter; }

    void SomeFunc() { m_inter->FuncToCall(); }

protected: // or `public:` since you are using `struct`
    IInterface* m_inter;
};

struct Derived : Base, IInterface
{
  //Derived() : Base(this) {} // not good to use `this` in the initialization list
    Derived() : Base() { m_inter = static_cast<IInterface*>(this); }

    FuncToCall() {}
};

答案 5 :(得分:0)

有趣的是,您可以稍后通过初始化来逃避它:

Derived::Derived(): Base()
{
  this->setInter(this);
}

很好,因为所有属性都已初始化。

这样您就不必改变整个设计,只是为了逃避警告。

但是,除非setInter对某些存储空间this不执行任何操作,否则您可能会访问未完全初始化的对象(因此存储hash值可能会很尴尬)