• Доступ к приватным полям через арифметику указателей?

    @oleghab
    В реальности если требуется обойти ограничение доступа, то это может быть сделано например так:
    /*
     * Класс Cls определен точно таким образом:
     *
     * struct Cls {
     * Cls(char c, double d, int i);
     * private:
     *     char c;
     *     double d;
     *     int i;
     * };
     *
     */
    
    namespace {
        // Cls_Double - точная копия Cls в смысле расположения данных.
        // Важный момент - данные в Cls НЕ скрыты, но видимы с ограничением доступа (что разные вещи)
        // Это позволяет делать дубликат класса всегда
        struct Cls_Double {
            char c;
            double d;
            int i;
        };
    }
    
    // Эта функция должна предоставить доступ к полю c объекта cls.
    // Обратите внимание, что возвращается ссылка на char, т. е.
    // доступ предоставляется на чтение и запись.
    char &get_c(Cls &cls) {
        return reinterpret_cast<Cls_Double&>(cls).c;
    }
    
    // Эта функция должна предоставить доступ к полю d объекта cls.
    // Обратите внимание, что возвращается ссылка на double, т. е.
    // доступ предоставляется на чтение и запись.
    double &get_d(Cls &cls) {
        return reinterpret_cast<Cls_Double&>(cls).d;
    }
    
    // Эта функция должна предоставить доступ к полю i объекта cls.
    // Обратите внимание, что возвращается ссылка на int, т. е.
    // доступ предоставляется на чтение и запись.
    int &get_i(Cls &cls) {
        return reinterpret_cast<Cls_Double&>(cls).i;
    }


    Пара замечаний:
    1) приведение через арифметику указателей и reinterpret_cast оба нарушают strict aliasing, поэтому без разницы что использовать
    2) если есть внутренние терзания, то для хотя бы относительного душевного спокойствия можно использовать двойной static_cast вместо reinterpret_cast, тоже нарушит strict aliasing, так что всё равно
    3) Возможно, если это академическое задание, ожидается ручное вычисление через арифметику указателей, в стиле
    return *(char *)((std::uint8_t*)&cls);
    return *(double *)((std::uint8_t*)&cls + sizeof(char));
    return *(int *)((std::uint8_t*)&cls + sizeof(char) + sizeof(double));


    Но никогда так не делайте в проде, потому как можно заиметь кучу проблем с поддежкой таких вычислений, в том числе если вдруг Cls на самом деле будет наследоваться от класса с vptr, который при этом снаружи может измениться. Тогда Cls_Double нужно идентично наследовать, а компилятор обеспечит корректные адресации.
    Ещё один вариант - вопросы упаковки данных, который тоже компилятор может бесплатно решить за нас.
    Ответ написан
    Комментировать