Закрытые члены класса, модифицированные при создании структуры (C ++) - PullRequest
3 голосов
/ 29 сентября 2010

Я просто просматривал некоторые коды C ++. Где я наткнулся на концепцию reinterpret_cast оператора.

РЕДАКТИРОВАТЬ 1:

Я знаю, что доступ к закрытым членам класса не рекомендуется. Но в некоторых ситуациях мы должны идти вперед и получить к ним доступ. Я только что задал этот вопрос, чтобы прояснить мои концепции.

В приведенном мной примере доступ к закрытому члену класса осуществляется путем простого создания структуры с теми же переменными, а затем ее изменения путем реализации. reinterpret_cast оператор.

Я понял использование оператора reinterpret_cast, поскольку я знаю , что делает, но я не понимаю , как структура может использоваться для изменения значений Личный член класса .

Ниже приведен исходный код, на который я ссылался:

Класс:

class Student
{
public:
    explicit Student(float percent) // Cannot be used for conversion
    {
        static int nid;

        id = ++nid;
        score = percent;
    }

    int Id() const
    {
        return id;
    }

    float GetScore() const
    {
        return score;
    }

    void SetScore(float value)
    {
        score = value;
    }

    virtual ~Student(){}

private:
    int id;
    float score;
};

Структура, используемая для доступа и изменения закрытых членов класса:

struct _Student
    {
        void* vptr;
        int id;
        float score;
    };

    _Student* bs3 = reinterpret_cast<_Student*>(bs2);
    bs3->id = 5;

Спасибо. Пожалуйста, поправьте меня, если я ошибаюсь / я не смог сформулировать свой вопрос соответствующим образом.

Ответы [ 5 ]

3 голосов
/ 29 сентября 2010

Но в некоторых ситуациях мы должны пойти дальше и получить к ним доступ.

И что, молюсь, это ситуации?

Кроме ошибок проектирования, я могуне вижу никого.Доступ к частным пользователям запрещен.Если вам нужен доступ, то предоставьте его легальными средствами, то есть либо сделайте членов более доступными, либо используйте модификаторы friend для доступа к ним контролируемым образом.

Нарушение системы проверки типов C ++ равносильно игре поверх:Вы обманываете компилятор, не ожидайте, что он будет работать с вами.С таким неопределенным поведением (то есть не только зависимым от платформы, но запрещенным по уважительным причинам) вы просто вызываете неприятности в виде очень трудных для отслеживания ошибок.

tl; dr : нет.Всегда.

Предостережение : есть одно исключение: у вас есть библиотека, к которой вы не можете получить доступ / изменить исходный код, и у вас нет никакого способа повлиять на ее дизайн интерфейса.И дополнительно вы можете быть уверены (как?), Что это никогда не изменится.В этом случае единственным выходом может быть взлом библиотеки с помощью таких трюков.

3 голосов
/ 29 сентября 2010

$ 5.2.10 / 2 - "Выражение целочисленного типа, перечисления, указателя или указателя на член может быть явно преобразовано в его собственный тип; такое приведение дает значение своего операнда."

Это означает, что указатели 'bs2' и 'bs3' указывают на одно и то же местоположение

$ 9.2 / 16 -" Две структуры стандартного макета(Раздел 9) типы совместимы с макетом, если они имеют одинаковое количество элементов нестатических данных, а соответствующие элементы нестатических данных (в порядке объявления) имеют типы, совместимые с макетом (3.9). "

Это означает, что ваш класс и структура совместимы с макетом.

$ 9 / 6-

Класс стандартного макета - это класс, который:

- не имеет нестатических членов-данных типа нестандартного класса макета (или массива таких типов) или ссылки,

- не имеет виртуальных функций (10.3) и виртуальных базовых классов (10.1),

- имеет одинаковый контроль доступа (пункт 11) для всех нечлены данных atic,

- не имеют базовых классов нестандартной компоновки,

- либо не имеют нестатических элементов данных в наиболее производном классе, но не более одного базового класса с не-статические элементы данных или не имеет базовых классов с нестатическими элементами данных, а

- не имеет базовых классов того же типа, что и первый нестатический элемент данных.108

Поскольку в вашем классе есть виртуальный деструктор, ваш класс и структура не являются стандартными классами макета.

Однако вы добавили элемент данных 'void *', чтобы позаботиться о 'vptr' (таким образом возможно имитируясовместимость компоновки на основе вашей конкретной реализации компилятора)

В этом случае reinterpret_cast используется для интерпретации указателя класса (bs2) как указателя структуры (bs3).По умолчанию члены структуры являются публичными.Поскольку возвращаемое значение переинтерпретировать приведение указывает на ту же память (см. Цитату выше), где расположены члены класса, вы можете изменять члены структуры (которые совпадают с исходными членами класса).

Это обман,Это очень обескураживает.Скорее всего, это приведет к неопределенному поведению

2 голосов
/ 29 сентября 2010

Я думаю, вам, возможно, следует несколько изменить контекст вашего вопроса.Если вы понимаете, что вам нужен доступ к закрытой переменной в вашем классе, тогда вы сталкиваетесь с проблемой проектирования, которая должна быть решена, а не взломана с помощью небезопасного преобразования типов.Даже если это только гипотетически и ради вопроса о reinterpret_cast здесь.

Что касается случая использования reinterpret_cast, который имеет смысл, я бы сказал внутри хеш-функции:

unsigned short Hash( void *p ) {</p> <pre><code>unsigned int val = reinterpret_cast<unsigned int>( p ); return ( unsigned short )( val ^ (val >> 16));

}

Некоторые ссылки с полезной информацией:

Когда следует использовать static_cast, dynamic_cast, const_cast и reinterpret_cast?

http://advancedcppwithexamples.blogspot.com/2010/02/reinterpretcast-in-c.html

http://www.linuxtopia.org/online_books/programming_books/thinking_in_c++/Chapter03_054.html

1 голос
/ 29 сентября 2010

То, что у вас есть, это ужасный взлом вокруг инкапсуляции. Если вы действительно хотите получить доступ к закрытым переменным, вам следует использовать ключевое слово "друг". Причина, по которой работает reinterpret_cast, заключается в том, что вы интерпретируете байты класса Student как структуру _Student, переменные которой по умолчанию объявлены как public. Есть много способов плохого доступа к частным данным, вот еще один, который я могу придумать:

int* bs3 = reinterpret_cast<int*>(bs2);
++bs3;
*bs3 = 5;

Только не делай этого, это мой совет.

1 голос
/ 29 сентября 2010

Но в некоторых ситуациях мы должны идти вперед и получить к ним доступ.

Если вам необходимо получить к ним доступ в в любой ситуации , измените его спецификацию доступа.

Или, лучше, сделать публичный метод, который будет принимать токен (или некоторые специальные разрешения - для проверки вызывающего) и возвращать запрошенное значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...