2016-06-11 4 views
1
  • Имеет ли следующий код определенное поведение?
  • Если нет, то какая часть кода UB и какие разделы стандартного состояния UB?
  • Если этот код UB, есть ли [незначительные] изменения, которые могут его исправить?
  • Если ничего не исправить, какую другую схему/шаблон кода можно использовать для реализации той же функции?

class C 
{ 
public: 
    virtual ~C() {} 
    virtual void switch_me() = 0; 
}; 

class C1 : public C 
{ 
public: 
    C1() : b(true)  { std::cout << "C1\n"; } 
    ~C1()    { std::cout << "~C1\n"; } 
private: 
    void switch_me(); 
    bool b; 
}; 

class C2 : public C 
{ 
public: 
    C2() : i(1)   { std::cout << "C2\n"; } 
    ~C2()    { std::cout << "~C2\n"; } 
private: 
    void switch_me(); 
    int i; 
}; 

void C1::switch_me() 
{ 
    this->~C1();   // lifetime of *this ends here 
    std::cout << "blih\n"; // execute some code that does 
          // not attempt to access object 
    new(this) C2();   // create a C2 instance in-place 
} 

void C2::switch_me() 
{ 
    this->~C2();   // lifetime of *this ends here 
    std::cout << "blah\n"; // execute some code... 
    new(this) C1();   // create a C1 instance in-place 
} 

class Cnt 
{ 
public: 
    Cnt()   { new(&storage) C1(); } 
    ~Cnt()   { (*this)->~C(); } 
    C* operator->() { return reinterpret_cast<C*>(&storage); } 
private: 
    char storage[std::max(sizeof(C1),sizeof(C2))]; 
}; 

int main() 
{ 
    Cnt c; 
    c->switch_me(); 
    c->switch_me(); 
    return 0; 
} 
+0

Код для 'switch_me' is **** <- Вставьте здесь эксплойт. Зачем ты это делаешь? –

+0

@ πάντα ῥεῖ: определенно не дубликат! в моем вопросе функция-член вызывает его DESTRUCTOR СОБСТВЕННОГО КЛАССА (т. е. это саморазрушение). – shrike

+0

@shrike Я не вижу принципиальной разницы. Прочитайте оба ответа. Об этом даже упоминается саморазрушение мест размещения «новых» экземпляров. –

ответ

3

Вы не Неопределенное поведение относительно switch_me функции: вы не доступ к объекту в любом случае после разрушения, и следующий доступ происходит на новый объект. Вы, возможно, UB, если сохранить указатель и ссылку на C объекта, возвращенного уу operator-> и использовать его после вызова switch_me за 3,8/7:

Если после жизни объекта закончилась и перед хранением который занятый объект повторно используется или освобождается, создается новый объект в месте хранения, в котором был загружен исходный объект, указатель, указывающий на исходный объект, ссылку, относящуюся к исходному объекту, или имя исходного объекта автоматически ссылаются на новый объект и, как только время жизни нового объекта запущено, могут использоваться для управления новым объектом, если:

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

У вас есть UB в другом месте, а именно, в вашем хранилище. Он имеет более слабое выравнивание, чем объект, который вы хотите разместить в нем, и это может вызвать проблемы с выравниванием. Используйте alignas ключевое слово, чтобы указать желаемое выравнивание:

alignas(C1) alignas(C2) char storage[std::max(sizeof(C1),sizeof(C2))]; 

Если два выравнивания спецификаторов применяются к тому же декларации, используются больше.

+0

Спасибо за ваш ценный ответ. В предыдущем проекте я определил в 'Cnt'' 'operator *()' возврат ссылки на хранилище; как вы заметили в своем ответе, это был UB, и я хотел убедиться, что новый дизайн не работает, используя 'operator ->()'. Что касается выравнивания, в реальном коде я использую 'std :: aligned_storage <>' вместо 'char []', так что да, в моем фрагменте это UB, но не в реальном коде. Я, вероятно, приму ваш ответ, если никто не узнает что-то еще UB где-то ... В любом случае, спасибо. – shrike