Доступ к частной виртуальной функции извне класса

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

class protoCalc
{
    private:
        int x; 
        int y;
        virtual int basicAddition()
        {
            return x + y;
        }
        virtual int basicMultiplication()
        {
           return x*y;
        }
public:
    protoCalc(){
        x = 14;
        y = 120;
    }
};

Доступ к x и y оказался достаточно простым; для этого я написал функцию (включая свои мысли о том, как это работает, правильно они или нет):

int private_member_Print(void* proto)
{
    protoCalc* medium = (protoCalc*)proto;
    protoCalc halfway = *medium;
    int* ptr = ((int *)(&halfway));
return ptr[1];

}

Вышеприведенное вернет значение x, если используется ptr[2], оно вернет значение y.

Теперь у меня есть два вопроса, первый из которых — на что указывает ptr[0]? Разве этот бит памяти не должен быть занят закрытым членом x, поскольку это первый член класса protoCalc? Если нет, то что занимает этот адрес?

Во-вторых, как мне получить доступ к виртуальным функциям? Моей первой интуицией было то, что адрес ptr[3] будет занят виртуальной функцией basicAddition(), а адрес ptr[4] — функцией basicMultiplication(), однако это не так. Когда это не подтвердилось, моей следующей мыслью было то, что ptr[0] содержит указатель на местоположение виртуальной таблицы-члена, содержащей две функции, которые я искал. Однако и это оказалось ложным.

Итак, как мне получить доступ к этим виртуальным функциям вне класса, поскольку я получил доступ к закрытым членам x и y? Очевидно, я мог бы изменить обстоятельства, чтобы облегчить задачу, но это лишило бы смысла проблему.


person LilLarryboy    schedule 02.11.2014    source источник
comment
Это все неопределенное поведение. Предоставьте общедоступные функции для доступа к закрытым членам, если это то, что вы хотите сделать.   -  person Jonathan Potter    schedule 02.11.2014
comment
Как только я вижу пустой указатель и приведение, я перестаю читать. Что бы ни последовало, это не пойдет на пользу моему мозгу.   -  person Neil Kirk    schedule 02.11.2014
comment
Меньше всего вас беспокоит, но ваши функции можно сделать константными. И если в классе есть виртуальные функции, вероятно, у него должен быть и виртуальный деструктор.   -  person Neil Kirk    schedule 02.11.2014
comment
Я, должно быть, не был особенно ясен в настройке проблемы. Единственный код, который я могу изменить, находится в моей функции private_member_Print. Все в классе должно оставаться как есть для цели решения   -  person LilLarryboy    schedule 02.11.2014
comment
На практике ptr[0], вероятно, указывает на указатель таблицы виртуальных функций, но на это нельзя полагаться. stackoverflow.com/ вопросы/10925115/   -  person Neil Kirk    schedule 02.11.2014
comment
Все это зависит от множества факторов, таких как компилятор, платформа, метод выделения для вашего объекта и множество других.   -  person Eric    schedule 02.11.2014


Ответы (1)


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

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

Вот пример этого в действии.

#include <iostream>

class Priv {
private:
    int i;
    void print( ) {
        std::cout << i << std::endl;
    }

public:
    Priv( ) : i( 100 ) {}

    void print_pub( ) {
        std::cout << "Public" << std::endl;
        this->print( );
    }
};

template<typename Tag>
struct result {
  /* export it ... */
  typedef typename Tag::type type;
  static type ptr;
};

template<typename Tag>
typename result<Tag>::type result<Tag>::ptr;

template<typename Tag, typename Tag::type p>
struct rob : result<Tag> {
  /* fill it ... */
  struct filler {
    filler() { result<Tag>::ptr = p; }
  };
  static filler filler_obj;
};

template<typename Tag, typename Tag::type p>
typename rob<Tag, p>::filler rob<Tag, p>::filler_obj;

struct Priv_f { typedef void ( Priv::*type )(); };
template class rob< Priv_f, &Priv::print >;

struct Priv_i { typedef int Priv::*type; };
template class rob< Priv_i, &Priv::i >;

int main( ) {
    Priv p;

    (p.*result<Priv_i>::ptr) = 1;
    (p.*result<Priv_f>::ptr)(); 

}

Объяснение классов шаблонов rob/result можно найти здесь. Он должен работать через компиляторы.

person Smith_61    schedule 02.11.2014
comment
Я думаю, что вопрос больше сосредоточен на том, как реализованы виртуальные функции. Вы пытаетесь это охватить, но не совсем правильно: это не определяется реализацией. Реализации не обязаны документировать, как они реализуют виртуальные функции или как они размещают классы. Остальное может иметь отношение к тому, что спрашивает ОП, но, я думаю, совсем не к тому, что интересует ОП. - person ; 02.11.2014
comment
Фактически, комментарий OP. Единственный код, который я могу изменить, находится в моей функции private_member_Print. совершенно явно отвергает этот подход. - person ; 02.11.2014
comment
Да, я знаю, что есть гораздо более простые способы приблизиться к тому, чего я пытаюсь достичь, поэтому я упомянул об этом в основном вопросе: D. Я использую визуальную студию и путем проб и ошибок обнаружил, что ptr[0] действительно содержит указатель на vtable, однако проблема, с которой я сейчас борюсь, заключается в том, чтобы узнать, как получить доступ к информации из vtable - person LilLarryboy; 02.11.2014
comment
@hvd У меня сложилось впечатление, что определенная реализация означает, что реализация решает, как вещи расположены в памяти. Не то, чтобы это требовалось документировать. Хотя я могу ошибаться. - person Smith_61; 02.11.2014
comment
@Smith_61 Smith_61 Определяется реализацией означает, что реализация требуется для ее документирования. То, что вы говорите, может быть разумным значением термина, просто это не то значение, которое используется в стандарте С++. Когда от реализации требуется сделать выбор, но не требуется документировать этот выбор, это называется неуказанным. - person ; 03.11.2014
comment
@hvd Хорошо, моя ошибка. Я отредактирую его, чтобы изменить это. - person Smith_61; 03.11.2014