этот указатель на конструктор базового класса? - PullRequest
4 голосов
/ 29 марта 2010

Я хочу реализовать производный класс, который также должен реализовывать интерфейс, в котором есть функция, которую базовый класс может вызывать. Следующее предупреждение выдает, поскольку передавать указатель 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() {}
};

Как лучше всего обойти это? Мне нужно предоставить интерфейс в качестве аргумента базовому конструктору, так как интерфейс не всегда является классом dervied; иногда это может быть совершенно другой класс.

Я мог бы добавить функцию в базовый класс SetInterface (IInterface * inter), но я бы хотел этого избежать.

Ответы [ 6 ]

4 голосов
/ 29 марта 2010

Вы не должны публиковать 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.

3 голосов
/ 29 марта 2010

Вы всегда можете отложить разыменование интерфейса:

<code>struct IInterface
{
    virtual ~IInterface();
    virtual void FuncToCall() =0;
};</p>

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

<p>class Derived: public Base, public IInterface
{
public:
private:
    virtual IInterface& GetInterface() { return *this; }
    virtual void FuncToCall();
};
2 голосов
/ 29 марта 2010

Существует ограниченный набор операций, которые вы можете выполнять (гарантировано стандартом) с указателем на еще неинициализированный объект, и хранение его для дальнейшего использования является одной из них. Компилятор, вероятно, предупреждает, так как легко использовать полученный указатель в Base.

Помните, что в большинстве случаев использование указателя для хранения, отличного от хранилища, будет неопределенным поведением, но приведенный выше код верен.

1 голос
/ 19 октября 2010

как насчет:

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

...

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

    FuncToCall() {}
};

в тот момент, когда мы получаем базовый конструктор, IInterface уже инициализирован, поэтому вызов me () безопасен и не содержит предупреждений

1 голос
/ 29 марта 2010

Что довольно забавно, так это то, что вы можете обойтись без него, инициализировав его позже:

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

хорошо, потому что все атрибуты были инициализированы.

Таким образом, вам не придется менять весь дизайн, чтобы избежать предупреждения.

Однако, если setInter ничего не делает с this, кроме некоторого хранилища, вы можете получить доступ к объекту, который вы не полностью инициализировали (поэтому сохранение значения hash может быть неудобным).

0 голосов
/ 29 марта 2010

как насчет использования 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() {}
};
...