Полиморфизм

185
19 декабря 2016, 19:45

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

Вот они:

  1. Есть некоторый класс, в котором обязательно есть 1 виртуальная функция, которая не работает с данными класса но что-то выводит на экран. Объявление класса есть. Как вызвать эту функцию не используя ее имя?

  2. Есть указатель на объект класса, и через него вызывается функция (p-> F ()). Как не меняя класса обеспечить, чтобы при вызове функции F (), вызывалась не она, а новая моя функция?

  3. Есть класс в котором объявлено функцию и элемент типа int I. через указатель вызывается функция, которая возвращает элемент. и он выводится на экран.

    cout << p-> GetI ();

Как сделать, чтобы при вызове функции GetI () вызывалась не она, а новая моя функция, которая изменит элемент int I умножив его на 10 и выведет на экран?

Добавлено. Я уточнил 2 и 3 задания.

  • Есть класс в котором объявлено метод и элемент типа int. Через указатель на объект класса вызывается метод, который возвращает значение элемента int. Как не меняя реализацию класса обеспечить , чтобы при вызове метода через указатель вызывался не он, а моя глобальная функция, которая изменит элемент пита int умножив его на 10?

Важно - наследование использовать нельзя; соответственно нельзя создавать новых классов.

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

Важно - наследование использовать нельзя; соответственно нельзя создавать новых классов.

Answer 1

Задания сформулированы ужасно, мои сочувствия вам.

1) Что означает «не используя ей имя»? Чтобы вызвать функцию, надо указать её имя в той или иной форме. То, работает функция с данными класса или нет, полностью несущественно.

Можно произвести от данного класса производный класс, не переопределять в нём нужную функцию, в написать другую функцию-обёртку.

class Given
{
public:
    virtual void func() { cout << "Done" << endl; }
};
class Derived : public Given
{
public:
    void wrapper() { func(); }        
}
// вызов без использования имени
Derived d;
d.wrapper();

Можно тупо заменить вызов макросом:

#define WRAP() func()
Given g;
g.WRAP();

По существу отличающихся корректных способов не вижу.

2) Если функция виртуальная, породите класс от данного и перекройте функцию.

class Given
{
public:
    virtual string func() { return "Given"; }
};
class Derived : public Given
{
public:
    virtual string func() { return "Overridden"; }
}
Given* p = new Derived;
cout << p->func();

Если нет, невозможно. Хотя подход с макросом пройдёт, если есть хоть какая-то виртуальная функция.

3) То же самое. Если функция GetI виртуальная, породите класс от данного и перекройте функцию. Если нет, корректно сделать невозможно.

Answer 2

1) Получите указатель на таблицу виртуальных функций. Вызовите функцию прямо по адресу.

Пример кода:

#include <iostream>
class A {
public:
    virtual void func() { std::cout << "Hello\r\n"; }
};
typedef void (*func_type)();
int main()
{
    A* a = new A;
    /*a->func();*/ // не вызываем по имени! 
    // указатель на таблицу виртуальных функций
    func_type* _vptr_table = *reinterpret_cast<func_type**>( a );       
    // указатель на первую и единственную функцию в таблице
    func_type f = _vptr_table[0]; 
    // вызываем по указателю!
    f();
    delete a;
    return 0;
}

Чтобы понять, что такое таблица виртуальных функций: Таблица виртуальных функций на Wikipedia

По 2) и 3) Согласен с @VladD. Унаследоваться и перекрыть виртуальную функцию.

Answer 3

Это не ответ.

Из ответа atwice:

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

Важный отрывок из условия задачи:

Есть некоторый класс, в котором обязательно есть 1 виртуальная функция, которая не работает с данными класса но что-то выводит на экран

Если это "литературно" разобрать, то

class A
{
public:
virtual void func() { std::cout << "Hello\r\n"; } // одна обязательная виртуальная функция
void func2(); // не обязательная не виртуальная функция
virtual void func3() { std::cout << "Hello3\r\n"; } // не обязательная вторая виртуальная функция
int D; // данные класса с которыми не работает func(). Значит они есть.
};

Правильный ответ atwice. Но если поменять местами func3 и func то ответ atwice не правильный. Так как будет вызвана func3().

Но если мы знаем в какой последовательности будут расположены виртуальные функции в объявлении класса, то ответ atwice всё же будет правильным, достаточно будет изменить _vptr_table[0] на _vptr_table[n] в зависимости от порядка расположения виртуальных функций с одинаковым прототипом, при условии поддержки данного механизма в конкретной версии среды разработки и соблюдении необходимых настроек компиляции.

Вывод:

Зная прототип этой обязательной виртуальной функции и порядок расположения в структуре виртуальных функций с таким же прототипом, мы можем вызвать эту функцию не зная её имени, используя таблицу виртуальных функций.

Однако если условие задачи автора на самом деле иное, и предполагается что нельзя делать вызов функции по имени которое объявлено в классе, то правильными будут ответы VladD и perfect.

Answer 4

По поводу первого пункта в вопросе. Проанализируйте это:

#include <iostream>
using namespace std;
class C{
public:
    virtual void draw(){    // настоящее имя
         cout << "hello" << endl;
    }
};
int main() {
    C * theClass = new C;
    void (C::*blabla)();
    blabla = &C::draw;
    (theClass->*blabla)(); // уже ненастоящее имя
    delete theClass;
    return 0;
}
READ ALSO
Присвоить значение элементу массива

Присвоить значение элементу массива

Только начал изучать С++, возникла сложностьНеобходимо присвоить определенному элементу массива значение

167
Генератор случайных чисел

Генератор случайных чисел

Не могу понять как заставить генератор случайных чисел давать дробные значенияПробовал сделать так

203