Вот, из Саттера — всевозможные на тот момент С++03 способы :)
Рассмотрим следующий заголовочный файл.
// Файл x.h
//
class X {
public:
X() : private_(1) { /*...*/ }
template<class T>
void f( const T& t ) { /*...*/ }
int Value() { return private_; }
// ...
private:
int private_;
};
Покажите, как произвольный вызывающий код может получить непосредственный доступ к закрытому члену класса private_
.
Преступник №1: фальсификатор
Метод фальсификатора состоит в том, чтобы продублировать определение фальсифицируемого класса, в которое внесены все необходимые нам изменения, например:
// Пример 15-1: ложь и подделка
//
class X {
// Вместо включения файла x.h, вручную (и незаконно)
// дублируем определение X и добавляем строку наподобие
// этой:
friend ::Hijack( X& );
};
void Hijack( X& x ) {
x.private_ = 2; // Злобный смех
}
Конечно, то, что сделано, — не законно. Это не законно хотя бы потому, что нарушает правило одного определения, которое гласит, что если тип (в нашем случае — X
) определен более одного раза, то все его определения должны быть идентичны. Используемый же в приведенном примере объект хотя и называется X
и выглядит похожим на X
, но это не тот же X
, который используется остальным кодом программы. Тем не менее, такой “хак” будет работать на большинстве компиляторов, поскольку расположение данных объекта останется неизменным.
Преступник №2: карманник
Карманник сработает тихо — подменив смысл определения класса, например, следующим образом.
// Пример 15-2: использование макромагии
//
#define private public // Не законно!
#include "x.h"
void Hijack( X& x ) {
x.private_ = 2; // Злобный смех
}
Этот человек — карманник. Запомните его умелые пальцы!
Конечно, то, что делает карманник, — не законно. Код из примера 15-2 непереносим по двум причинам.
Не законно переопределять при помощи директивы #define
зарезервированные слова.
При этом происходит такое же нарушение правила одного определения, как и у фальсификатора. Однако если расположение данных объекта остается неизменным, этот способ сработает.
Преступник №3: мошенник
Принцип работы мошенника — в подмене понятий.
// Пример 15-3: попытка имитации размещения данных объекта
//
class BaitAndSwitch {// В надежде, что размещение данных в
public: // этом классе будет тем же, что и в X
int notSoPrivate;
};
void f( X& x ) {
(reinterpret_cast<BaitAndSwitch&>(x)).notSoPrivate = 2;
} // Злобный смех
Этот человек — мошенник. Хорошенько его запомните! Его реклама рассчитана только на то, чтоб затащить вас в магазин, а уж там он обязательно ухитриться продать вам совершенно не ту вещь, о которой шла речь в рекламе, да еще и в несколько раз дороже, чем в любом другом месте.
Конечно, то, что делает мошенник, — не законно. Код из примера 15-3 не законен по двум причинам.
Нет гарантии, что размещение объектов X
и BaitAndSwitch
в памяти будет одинаковым — хотя на практике это обычно так и есть.
Результат reinterpret_cast
не определен, хотя большинство компиляторов сделают именно то, чего от них хочет мошенник. В конце концов, говоря волшебное слово reinterpret_cast
, вы заставляете компилятор поверить вам и закрыть глаза на подготавливаемое вами мошенничество.
Персона грата №4: адвокат
Многие из нас недаром больше боятся хорошо одетых и улыбающихся адвокатов, чем (других) преступников.
Рассмотрим следующий код.
// Пример 15-4: проныра-законник
//
namespace {
struct Y {};
}
template<>
void X::f( const Y& ) {
private_ = 2; // Злобный смех
}
void Test() {
X x;
cout << x.Value() << endl; // Выводит 1
x.f( Y() );
cout << x.Value() << endl; // Выводит 2
}
Этот человек — адвокат, который знает все лазейки. Его невозможно поймать, поскольку он слишком осторожен, чтобы нарушить букву закона, при этом нарушая его дух. Запомните и избегайте таких неджентльменов.
Как бы мне ни хотелось сказать “Конечно, то, что делает адвокат, — не законно”, увы, я не могу этого сделать, поскольку все сделанное в последнем примере законно. Почему? В примере 15-4 использован тот факт, что у X
есть шаблон функции-члена. Приведенный код соответствует стандарту, так что последний гарантирует, что он будет работать так, как ожидается.