C ++: странное поведение при инициализации данных члена - PullRequest
2 голосов
/ 11 апреля 2020

Я столкнулся со странным поведением в отношении C++17 static inline member data.
Существует Implementation абстрактный класс:

class Implementation
{
public:
  virtual void call() = 0;
};

Существуют Example и AnotherExample классы, которые реализуют абстрактный класс :

class Example : public Implementation
{
public:
  void call() override
  {
    cout << "Called Example::call function!" << endl;
  }
};

class AnotherExample : public Implementation
{
public:
  void call() override
  {
    cout << "Called AnotherExample::call function!" << endl;
  }
};

Наконец, есть класс Implementer для использования Example и AnotherExample классов:

class Implementer
{
private:
  static inline Implementation* example = unique_ptr<Example>(new Example).get(); // or make_unique<Example>().get()
  static inline Implementation* another_example = new AnotherExample;
public:
  static Implementation* get_example(bool flag)
  {
    // This function returns example object if flag is true otherwise another_example object.
    if (flag)
      return example;
    else
      return another_example;
  }
};

Использование:

Implementer::get_example(true)->call(); // This expect to return example.
Implementer::get_example(false)->call(); // This expect to return another_example.

Мы ожидаем чтобы увидеть это:

Called Example::call function!
Called AnotherExample::call function!

но мы видим это:

Called AnotherExample::call function!
Called AnotherExample::call function!

Я не понимаю этого. Как объект example получает значение объекта another_example?

Редактировать: Мы можем получить желаемый результат, если изменить

static inline Implementation* example = unique_ptr<Example>(new Example).get();

на

static inline unique_ptr<Implementation> example = unique_ptr<Example>(new Example);

1 Ответ

5 голосов
/ 11 апреля 2020

unique_ptr<Example>(new Example).get() уничтожит новый экземпляр Example, как только будет назначен результат get() (спасибо @JVApen за исправление).

С этого момента множество странных вещей может произойти.

Например, объект, выделенный new AnotherExample, может (и, скорее всего, будет) помещаться в ту же память, где находился первый, и первый указатель внезапно будет указывать на действительный живой объект снова.

Не уверен, что это квалифицируется как ABA феномен, но то, что происходит, очень похоже - вы думаете, что смотрите на А, но это другой А.

Я бы сначала очистил его под Valgrind или ASAN .

Песочница Coliru здесь

...