Могу ли я преобразовать объект и получить доступ к частным элементам данных в C ++?

Я хочу получить доступ к частному элементу данных в классе. В классе нет функции-члена для доступа к частному элементу данных. Это личное.

Я хочу пройти курс и кое-как его открыть. Один из методов заключался в том, чтобы скопировать объявление класса, сделать закрытый член общедоступным и вызвать новый класс class something_else. Затем я заново интерпретирую слепок и копирую исходный объект. Это работает. Но я хочу что-то более элегантное ... или, возможно, универсальное ... или просто другое.

Какие есть варианты? Могу ли я использовать void *? Могу ли я поместить этот класс в другой пустой класс? Какие есть способы сделать это ??

%


person Community    schedule 03.10.2009    source источник
comment
Как насчет редактирования источника? //private: будет работать нормально;)   -  person LiraNuna    schedule 03.10.2009
comment
Я думаю, что он хочет, чтобы пользователи его класса не возились (так частные), и ему нужно реализовать что-то, что действительно не вписывается в текущий дизайн.   -  person jdehaan    schedule 03.10.2009
comment
Я думаю, у вас нет кода, не так ли?   -  person Ahmed Said    schedule 03.10.2009
comment
@LiraNuna: комментирование тега «private:» будет работать в некоторых ситуациях, но может незаметно нарушить код в других. Вы можете тонко изменить тип, не относящийся к POD, на тип POD, и в языке есть несколько другие правила для этих типов.   -  person David Rodríguez - dribeas    schedule 03.10.2009
comment
Думаю, нам нужно знать, почему вам это нужно. Ответ во многом зависит от того, чего вы пытаетесь достичь.   -  person jalf    schedule 03.10.2009


Ответы (6)


Я предполагаю, что

  1. Вы уже прошли этап «нарушение инкапсуляции - это плохо»,
  2. Исчерпаны другие возможные решения,
  3. Не удается изменить заголовок класса.

Есть несколько способов нарушить доступ к закрытым членам класса, как показано в GotW № 76.

  1. Дублируйте определение класса и добавьте объявление friend.
  2. Используйте злые макросы: #define private public перед включением заголовка класса.
  3. Напишите определение класса с идентичным двоичным макетом и используйте reinterpret_cast для переключения с исходного класса на поддельный.
  4. Специализируйте функцию-член шаблона, если она есть (единственное переносимое решение).
person Alex B    schedule 03.10.2009
comment
Обожаю упоминание злых макросов;) - person Calyth; 03.10.2009

С идеей, которую вы предлагаете в своем вопросе, вам не нужно копировать исходный объект. Если вы напишете свой собственный «общедоступный» вариант объявления реального класса, а затем приведете указатель к этому новому типу, вы сможете напрямую обращаться к объекту через него.

Причина, по которой все это не является хорошей идеей, проста. Вы должны манипулировать объектами класса, источником которого вы не управляете (в противном случае вы могли бы изменить источник, чтобы предоставить вам необходимый доступ). Но если вы не контролируете источник, что, если сопровождающие изменят макет своего класса? Ваша дублированная версия больше не будет соответствовать, и компилятор не сможет обнаружить это несоответствие. Результатом, вероятно, будет повреждение памяти во время выполнения.

person Daniel Earwicker    schedule 03.10.2009

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

#include <class.h>
ObjectFoo instance;

возможно (но опасно, если вы не будете осторожны) изменить заголовок (a) или скопировать заголовок в другое место и включить этот заголовок (b), без перекомпиляции самого класса < / сильный>.

#include <class_fixed.h>
ObjectFoo instance;

Ваш код, в который вы включили новый заголовок, будет просто думать, что в объектном файле (который вы не перекомпилировали!) Он найдет реализацию класса, объявленного как в class_fixed.h. Пока сохраняется класс, объявленный как в class.h. Если вы измените смещения элементов (например, добавите новые элементы) в своем новом заголовке, вы мертвы, и код не будет работать должным образом. Но просто изменение доступа работает нормально. Скомпилированный код не знает о доступе, это имеет значение только при компиляции странно.

Это не всегда вредно. В повседневной жизни вы сталкиваетесь с таким изменением, когда устанавливаете новую версию библиотеки в свою систему и не перекомпилируете все программы, которые от нее зависят. Но с этим нужно обращаться осторожно


Есть несколько решений.

  1. memcpy()
    Не надо! Не используйте memcpy, поскольку копирование объектов иногда подчиняется определенной политике, наложенной разработчиком класса. Например, auto_ptrs нельзя просто скопировать в память: если вы скопируете в память auto_ptr, а затем деструктор будет запущен для обоих, вы попытаетесь освободить одну и ту же память два раза, и программа выйдет из строя.

  2. Измените private: на public: в заголовке или с помощью макроса
    Если ваша лицензия позволяет, вы можете решить свою проблему, отредактировав файл заголовка, который поставляется с реализацией класса. Независимо от того, находится ли исходный код реализации (т.е. cpp-файл класса) под вашим контролем, не имеет значения: достаточно изменить частный на общедоступный для элементов данных (в заголовке) и отлично работает, даже если вам дан двоичный- единственная библиотека, содержащая определение класса. (Для функций-членов изменение доступа иногда меняет свое внутреннее имя, но для MSVS и GCC это нормально.)

  3. Добавление новой функции-получателя
    Хотя изменение private на public почти всегда нормально (если вы не полагаетесь на определенные проверки во время компиляции, которые должны прервать компиляцию, если у класса есть доступный определенный член), добавление новой функции-получателя следует выполнять осторожно. Функция-получатель должна быть встроенной (и, следовательно, определяться в файле заголовка класса).

  4. reinterpret_cast
    Приведение работает нормально, если вы НЕ приводите указатель к динамическому базовому классу (динамический означает «с виртуальными функциями или базами»), фактический экземпляр которого находится в момент литья может быть получен из класса в конкретном фрагменте кода.

  5. protected:
    И на всякий случай забыл. C ++ может объявлять элементы protected:, т.е. доступные только для классов, производных от данного. Это может удовлетворить ваши потребности.

person P Shved    schedule 03.10.2009

Можно, но нельзя. Объекты - это просто память. Вы, безусловно, можете преобразовать указатель в эквивалентный класс, который имеет те же члены, но где все является общедоступным. Но почему вы хотите это сделать? У вас есть чужой код, с которым вам нужно работать? Заставьте их добавить правильные методы доступа. Вам действительно нужно относиться к ним как к публичным членам? Смените класс.

Я не совсем уверен, что вы пытаетесь сделать, но, вероятно, это ошибка.

person vy32    schedule 03.10.2009

Я согласен с комментарием «отредактируйте источник», но я думаю, вам следует добавить метод, а не просто закомментировать «частный».

У вас должно быть объявление класса, поэтому у вас, вероятно, есть заголовок, но, возможно, не файл .cpp / любой другой. Добавьте в класс встроенную функцию-член в копии заголовка и включите этот заголовок вместо оригинала. Вы по-прежнему должны иметь возможность ссылаться на объектный файл для недоступного исходного кода.

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

Помните, что если вы работаете с другим классом elses, следующая версия может быть реализована иначе и может вообще не иметь этого члена.

person Steve314    schedule 03.10.2009

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


#include<iostream>
using namespace std;

// Class Objectfoo
// Pretend Objectfoo lives somewhere else ... I cannot open him up

class ObjectFoo 
{
  private:
  int datax; 
  public:
   ObjectFoo() { datax = 100; }
   void get() { cout << datax << endl;}
};

// Class ObjectBar
class ObjectBar 
{
  public:
   int datax;
};

ObjectFoo FOOEY;

ObjectBar* touch_foo(int x, ObjectFoo* foo , ObjectBar* bar)
{
 bar = reinterpret_cast<ObjectBar*>(foo);
 bar->datax = x;
 return bar;
}

int main() 
{
  ObjectBar* bar;

  cout << "Displaying private member in ObjectFoo i.e. ObjectFoo.datax" << endl;
  FOOEY.get();

  cout << "Changing private member " << endl;
  bar = touch_foo(5, &FOOEY, bar);

  cout << "bar->datax = " << bar->datax << endl;

  cout << "Displaying private member in ObjectFoo i.e. ObjectFoo.datax" << endl;
  FOOEY.get();

  return 0;
}

Это работает ... но я думаю, что мне нужно что-то более общее ... или более гибкое.

%

person Community    schedule 03.10.2009
comment
Во многих ответах здесь предполагалось, что вы не можете изменить исходный код .cpp, в то время как вы можете изменить - или просто скопировать, переименовать и повторно включить - файл заголовка. Перечитайте ответы с этим пониманием. - person P Shved; 03.10.2009
comment
Гораздо лучше добавить эту информацию к исходному вопросу, чем добавлять ее в качестве ответа. - person KeithB; 03.10.2009