CRTP вызывает segfault

У меня есть чистый виртуальный класс Interface:

class Interface {
  public:
    virtual ~Interface() noexcept;
    virtual void open()=0;
    virtual void close()=0;
  protected:
    explicit Interface(const string params);
    string params_;
}

Затем у меня есть абстрактный класс, в котором я реализую свою бизнес-логику:

template<typename T>
class AbstractInterface : public Interface {
  public:
    void open() override;
    void close() override;
    void read_is_complete(const vector<byte_array>);
  protected:
    explicit AbstractInterface(const string params);
    virtual ~AbstractInterface() noexcept;
}

Далее идет реализация интерфейса, использующего CRTP для полиморфизма:

class SPInterface : public AbstractInterface<SPInterface> {
  public:
    explicit SPInterface(const string params);
    virtual ~SPInterface() noexcept;
    void open();
    void close();
    void read_is_complete(const vector<byte_array> data);
}

У меня есть модульный тест, в котором я создаю экземпляр SPInterface:

unique_ptr<Interface> intf;
intf.reset(new SPInterface("aaa"));

Если позволить этому выйти за рамки, вызывается деструктор AbstractInterface, который, в свою очередь, вызывает метод close для AbstractInterface, а затем segfaults для this:

template<typename T>
void AbstractInterface<T>::close() {
  static_cast<T *>(this)->close();
  params_ = "";
}

Что сбивает с толку, поскольку я уже создал экземпляр класса. lldb, похоже, подтверждает:

AbstractInterface<SPInterface>::close(this=<unavailable>)

person ruipacheco    schedule 01.12.2016    source источник
comment
Безопасно ли пытаться вызывать метод производного класса из деструктора базового класса?   -  person skypjack    schedule 01.12.2016
comment
Я думаю, что вы имеете точку там :)   -  person ruipacheco    schedule 01.12.2016


Ответы (1)


Если позволить этому выйти за пределы области действия, вызывается деструктор AbstractInterface, который, в свою очередь, вызывает метод close для AbstractInterface, а затем выдает ошибку segfault:

template<typename T>
void AbstractInterface<T>::close() {
    static_cast<T *>(this)->close();
    params_ = "";
}

Похоже, вы пытаетесь вызвать метод производного класса из деструктора базового класса.
Это совсем небезопасно, и segfault — это то, как исполняемый файл должен сообщить об этом. вы, что это не одобряет. :-)

Несмотря на то, что CRTP позволяет вызывать функцию-член, принадлежащую производному классу, для (скажем так) живого объекта, это не меняет способ уничтожения объекта.
Не делайте этого. забудьте, что базы и члены уничтожаются в порядке, обратном завершению их конструктора.

person skypjack    schedule 01.12.2016