Член парламента, не занимающий официального поста взламывает определенное поведение?

У меня есть следующий класс:

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;

Я знаю, что это - плохая практика. Но только из любопытства: это, как гарантируют, будет работать? Это - определенное поведение?

Вопрос о премии: необходимо ли было когда-либо использовать такой ужасный взлом?

Править: Только испугать меньше людей: Я не намереваюсь на самом деле сделать это в реальном коде. Я просто задаюсь вопросом ;)

22
задан AstroCB 31 August 2014 в 01:42
поделиться

6 ответов

Это неопределенное поведение. Члены в каждой секции access-квалификатора гарантированно располагаются в порядке их появления, но между acccess-квалификаторами такой гарантии нет. Например, если компилятор решит расположить все приватные члены перед всеми публичными членами, то два вышеприведенных класса будут иметь разное расположение.

Edit: Пересмотрев этот старый ответ, я понял, что упустил довольно очевидный момент: определения struct имеют ровно по одному члену данных. Порядок функций-членов не имеет значения, поскольку они не вносят вклад в компоновку класса. Вы вполне можете обнаружить, что оба члена данных гарантированно находятся в одном и том же месте, хотя я не знаю стандарт достаточно хорошо, чтобы сказать наверняка.

Но! Вы не можете разыменовать результат reinterpret_castинга между несвязанными типами. Это все еще UB. По крайней мере, таково мое прочтение http://en.cppreference.com/w/cpp/language/reinterpret_cast, а это действительно ужасное прочтение.

21
ответ дан 29 November 2019 в 04:50
поделиться

Это неопределенное поведение, по причинам, указанным Марсело. Но иногда приходится прибегать к таким вещам при интеграции внешнего кода, который вы не можете модифицировать. Более простой способ сделать это (и такое же неопределенное поведение):

#define private public
#include "BritneySpears.h"
9
ответ дан 29 November 2019 в 04:50
поделиться

Возможно, вы не сможете изменить библиотеку для 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; }; 
}; 

Я не могу одобрить этот трюк, и не думаю, что когда-либо пробовал его сам, но это должно быть законным хорошо определенным C++.

4
ответ дан 29 November 2019 в 04:50
поделиться

@Marcelo прав: порядок членов не определен на разных уровнях доступа.

Но рассмотрим следующий код; здесь 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;
}

Я считаю, что это действительно может быть действительным C ++.

2
ответ дан 29 November 2019 в 04:50
поделиться

В вашем коде есть проблема, подчеркнутая ответами. Проблема возникает из-за упорядочивания значений.

Однако вы почти угадали:

class AshtonKutcher
{
public:

  int getValue() const { return m_value; }
  int& getValue() { return m_value; }

private:
  int m_value;
};

Теперь у вас точно такая же схема, потому что у вас одинаковые атрибуты, объявленные в одинаковом порядке и с одинаковыми правами доступа... и ни у одного из объектов нет виртуальной таблицы.

Таким образом, хитрость заключается не в изменении уровня доступа, а в добавлении метода :)

Если, конечно, я ничего не упустил.

Я точно сказал, что это кошмар в плане обслуживания?

2
ответ дан 29 November 2019 в 04:50
поделиться

Использования reinterpret_cast обычно следует избегать, и оно не гарантирует переносимых результатов.

Кроме того, почему вы хотите изменить частный член? Вы могли бы просто обернуть исходный класс в новый (предпочитаю композицию наследованию) и обработать метод getValue так, как вам нужно.

1
ответ дан 29 November 2019 в 04:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: