Что происходит здесь, в этой программе на C++?

Я читал эту прекрасную статью Использование и злоупотребление правами доступа. . Я не понял следующий пример из этого.

Файл: x.h

class X 
{ 
public:
  X() : private_(1) { /*...*/ }

  template<class T>
  void f( const T& t ) { /*...*/ }

  int Value() { return private_; }

private: 
  int private_; 
};

Файл: break.cpp

#include "x.h"
#include <iostream>
class BaitAndSwitch
    // hopefully has the same data layout as X
{   // so we can pass him off as one
  public:
  int notSoPrivate;
};

void f( X& x )
{
  // evil laughter here
  (reinterpret_cast<BaitAndSwitch&>(x)).notSoPrivate = 2;
}
int main()
{
    X x;
    std::cout<<x.Value()<<'\n';
    f(x);
    std::cout<<x.Value()<<'\n';
}

Как работает эта программа? Что на самом деле происходит в глобальной функции f()? Пожалуйста, кто-нибудь ясно объясните, как изменяется значение частной переменной?

Почему Херб Саттер сказал, что макеты объектов X и BaitAndSwitch не обязательно будут одинаковыми, хотя на практике они, вероятно, всегда будут такими? Хорошо ли определена эта программа?


person Destructor    schedule 29.09.2015    source источник
comment
Я думаю, что reinterpret_casting указатель на указатель на «неправильный» объект вызывает неопределенное поведение. Этот UB может оказаться именно тем, что вы хотели, но бессмысленно рассуждать и опасно полагаться на него.   -  person 5gon12eder    schedule 29.09.2015
comment
Эта программа не работает; его поведение не определено. Все может случиться, и вы видите конкретную реализацию чего угодно.   -  person Kerrek SB    schedule 29.09.2015
comment
@KerrekSB: он выдает ожидаемый результат на моей локальной машине. Что за яз. спецификация говорит об этом?   -  person Destructor    schedule 29.09.2015
comment
Вероятно, он сказал, что макеты не обязательно будут одинаковыми, потому что это не так. Наверное будет на практике не гарантия.   -  person molbdnilo    schedule 29.09.2015
comment
Он переопределяет частное поле, слепо приводя один класс только с одним целым числом к ​​другому классу только с одним целым числом. Компилятор получает команду: поставить 2 в поле объекта класса BaitAndSwitch. Поскольку во время выполнения нет классов, компилятор переводит это так, чтобы записать 2 в память по адресу ‹x› со смещением ‹0›, ноль, потому что это первое поле класса в обоих классах.   -  person    schedule 29.09.2015
comment
@PravasiMeet: Даже твое ожидание — это реализация чего-либо, возможно, самого опасного из всех.   -  person Kerrek SB    schedule 29.09.2015
comment
@PravasiMeet, какого поведения вы ожидаете? Стандарт C++ не определяет, что произойдет в этих обстоятельствах, поэтому не следует ожидать какого-либо поведения.   -  person robert    schedule 29.09.2015
comment
Результат изменится, если вы добавите или удалите или переупорядочите поля или добавите виртуальные члены в класс X.   -  person    schedule 29.09.2015
comment
@Satus: Да, интересный момент. Спасибо. Я добавил виртуальную функцию в класс X, и теперь значение частной переменной остается неизменным. Почему? Это из-за секретного добавления vptr в класс X, поэтому его расположение объектов теперь отличается от класса BaitAndSwitch?   -  person Destructor    schedule 29.09.2015
comment
Чтобы это работало, классы/структуры должны иметь одинаковую компоновку. Добавление виртуальной функции изменяет макет, потому что добавляет скрытый указатель на виртуальную таблицу (это делает только первая добавленная виртуальная функция — все последующие виртуальные функции повторно используют один и тот же указатель).   -  person PSkocik    schedule 29.09.2015
comment
@PravasiMeet Потому что, если у вашего класса есть виртуальные функции, в памяти будет создана виртуальная таблица для вашего класса, и каждый объект вашего класса будет хранить указатель на эту таблицу в качестве первого невидимого члена. Если вам нужны дополнительные сведения о том, зачем это нужно и как это работает, поищите в google C++ dynamic polymorphous.   -  person    schedule 29.09.2015
comment
Чтобы добавить к тому, что говорит @KerrekSB, в статье несколько раз используются слова незаконно и не определено. Поэтому я думаю, что из статьи ясно, что это неопределенное поведение, и поэтому у нас не может быть никаких ожиданий относительно результатов.   -  person Shafik Yaghmour    schedule 29.09.2015
comment
Страница, которую вы процитировали, сама рассказывает, что происходит: The code in Example 3 is illegal for two reasons: a) The object layouts of X and BaitAndSwitch are not guaranteed to be the same, although in practice they probably always will be. b) The results of the reinterpret_cast are undefined, although most compilers will let you try to use the resulting reference in the way the hacker intended.   -  person hlscalon    schedule 29.09.2015
comment
На статью также несколько раз ссылаются в предыдущие вопросы SO вы читали их в первую очередь?   -  person Shafik Yaghmour    schedule 29.09.2015
comment
Ну, это не определено с точки зрения языка C++ и компилятора, оно не (в данном случае) не определено для программиста. Хотя да, никогда не используйте этот тип хаков в реальных программах, они хороши только для развлечения.   -  person    schedule 29.09.2015
comment
IMO, если это не работает для вас, вы можете пересмотреть свой выбор компилятора. Потому что ваш либо совсем невменяемый, либо очень умный в каком-то неочевидном смысле. В любом случае, вы всегда можете получить доступ к закрытым переменным, если вы действительно этого хотите, и также должна быть возможность получить доступ к ним определенным образом (проверьте макет целых чисел, используйте указатель char, преобразуйте массив указателей char в int как согласно вашему обнаруженному внутреннему макету).   -  person PSkocik    schedule 29.09.2015
comment
@PSkocik #define private public работает лучше, просто говорю xD   -  person    schedule 29.09.2015
comment
Было бы полезно, если бы вы сказали нам, что именно вы не понимаете. В конце концов, все объяснено на странице, которую вы цитируете.   -  person Christian Hackl    schedule 29.09.2015
comment
@Satus: в статье уже показан этот трюк. См. раздел Преступник № 2: Карманник.   -  person Christian Hackl    schedule 29.09.2015


Ответы (2)


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

На самом деле это ничем не отличается от захвата указателя и записи в него чего-либо, хотя это делается немного более точным способом.

person Jiminion    schedule 29.09.2015

См. [http://en.cppreference.com/w/cpp/language/reinterpret_cast]

Вы в основном говорите компилятору, что ваш объект на самом деле является объектом BaitAndSwitch, и он действительно будет делать то, что вы ему сказали :-)

person rockworm    schedule 29.09.2015