Частный член взломал определенное поведение?
У меня есть следующий класс:
class BritneySpears
{
public:
int getValue() { return m_value; };
private:
int m_value;
};
Какая внешняя библиотека (которую я не могу изменить). Я, очевидно, не могу изменить значение m_value
, только прочитал его. Даже вывод из BritneySpears
не будет работать.
Что делать, если я определяю следующий класс:
class AshtonKutcher
{
public:
int getValue() { return m_value; };
public:
int m_value;
};
И затем выполните:
BritneySpears b;
// Here comes the ugly hack
AshtonKutcher* a = reinterpret_cast<AshtonKutcher*>(&b);
a->m_value = 17;
// Print out the value
std::cout << b.getValue() << std::endl;
Я знаю, что это плохой. Но просто из любопытства: гарантировано ли это? Определено ли это поведение?
Бонусный вопрос: вам когда-нибудь приходилось использовать такой уродливый хак?
Изменить: просто чтобы напугать меньше людей: я не собираюсь на самом деле делать это в реальном коде. Мне просто интересно;)
Ответы
Ответ 1
Это поведение undefined. Члены в каждом разделе классификатора доступа должны быть выложены в том порядке, в котором они появляются, но нет никакой гарантии между квалификаторами квалификации. Например, если компилятор решит разместить всех частных членов перед всеми публичными членами, эти два класса будут иметь разный макет.
Изменить: Повторяя этот старый ответ, я понял, что пропустил довольно очевидный момент: определения структуры имеют ровно один член данных. Порядок функций-членов не имеет значения, поскольку они не вносят вклад в макет класса. Вы вполне можете найти, что оба элемента данных гарантированно находятся в одном и том же месте, хотя я не знаю, что стандарт достаточно хорошо, чтобы сказать наверняка.
Но! Вы не можете разыгрывать результат reinterpret_cast
между несвязанными типами. Это все еще UB. По крайней мере, это мое чтение http://en.cppreference.com/w/cpp/language/reinterpret_cast, которое действительно является gnarly.
Ответ 2
Это поведение undefined, по причинам Marcelo. Но иногда вам нужно прибегать к таким вещам при интеграции внешнего кода, который вы не можете изменить. Более простой способ сделать это (а также поведение undefined):
#define private public
#include "BritneySpears.h"
Ответ 3
Возможно, вы не сможете изменить библиотеку для BritneySpears
, но вы должны иметь возможность изменять файл заголовка .h. Если это так, вы можете сделать AshtonKutcher
другом BritneySpears
:
class BritneySpears
{
friend class AshtonKutcher;
public:
int getValue() { return m_value; };
private:
int m_value;
};
class AshtonKutcher
{
public:
int getValue(const BritneySpears & ref) { return ref.m_value; };
};
Я не могу потворствовать этому трюку, и я не думаю, что я когда-либо пробовал это сам, но это должен быть законный четко определенный С++.
Ответ 4
@Marcelo имеет право: порядок членов undefined на разных уровнях доступа.
Но рассмотрим следующий код; здесь AshtonKutcher
имеет тот же макет, что и BritneySpears
:
class AshtonKutcher
{
public:
int getValue() { return m_value; };
friend void setValue(AshtonKutcher&, int);
private:
int m_value;
};
void setValue(AshtonKutcher& ac, int value) {
ac.m_Value = value;
}
Я считаю, что это может быть действительно С++.
Ответ 5
Существует проблема с вашим кодом, подчеркнутая ответами. Проблема связана с упорядочением значений.
Однако вы были почти там:
class AshtonKutcher
{
public:
int getValue() const { return m_value; }
int& getValue() { return m_value; }
private:
int m_value;
};
Теперь у вас есть тот же самый макет, потому что у вас есть те же атрибуты, объявленные в том же порядке и с теми же правами доступа... и ни у одного объекта нет виртуальной таблицы.
Таким образом, трюк не должен изменить уровень доступа, но добавить метод:)
Если, конечно, я что-то пропустил.
Я точно уточнил, что это кошмар для обслуживания?
Ответ 6
Использовать reinterpret_cast обычно следует избегать, и он не гарантирует, что переносит результаты.
Кроме того, почему вы хотите изменить частного пользователя? Вы можете просто обернуть исходный класс в новый (предпочитаете состав над наследованием) и обрабатывать метод getValue по своему усмотрению.