Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> механизм виртуальных функций (не работает:(), иллюстрация классическим СИ 
V
    Опции темы
Podarochek
Дата 23.5.2008, 02:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 2.11.2007

Репутация: нет
Всего: нет



подскажите ошибку? smile 

Код

// Иллюстрация механизма виртуальных функций «классическим» Си
// Выделены компоненты, создаваемые транслятором
class A {
    void (**ftable)(); // Указатель на массив указателей
public: // виртуальных функций (таблицу функций)
    virtual void x(){}
    virtual void y(){}
    virtual void z(){}
    A();
    ~A(){} 
};
#define vx 0 // Индексы в массиве
#define vy 1 // указателей на виртуальные функции
#define vz 2 //
// Массив указателей функций класса А
void (*TableA[])() = { A::x, A::y, A::z };
A::A()

    ftable = TableA; /*...*/ // Назначение таблицы для класса А
}
class В : public A {
public:
    void x(){}
    void z(){}
    В();
    ~В(){}
};
// Массив адресов функций класса А в В
// А::у - наследуется из А, В::х - переопределяется в В
void (*TableB[])() = { В::x, A::y,В::z };
В::В()

    A::ftable = TableB; /*...*/ // Назначение таблицы для класса В
}
void main()
{
    
    В nnn; // Ссылается на объект производного класса В
    A *р = &nnn; // Указатель р базового класса А
    р->z();  // Реализация - (*(p->ftable[vz]))();
}



PM MAIL   Вверх
mes
Дата 23.5.2008, 03:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Podarochek @  23.5.2008,  02:37 Найти цитируемый пост)
подскажите ошибку? 

Код

   void (**ftable)(); // указатели на статическую функцию
  
   virtual void x(){}
    virtual void y(){}
    virtual void z(){} // так они уже виртуальные )) 


A::ftable = TableB; // a ftable разве не в private: ? 



--------------------
PM MAIL WWW   Вверх
Podarochek
Дата 23.5.2008, 03:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 2.11.2007

Репутация: нет
Всего: нет



что значит эта ошибка:

Error    1    error C2440: 'initializing' : cannot convert from 'void (__thiscall A::* )(void)' to 'void (__cdecl *)(void)'

Добавлено через 3 минуты и 30 секунд
тут же все пом ок:

Код

void (*TableA[])() = { A::x, A::y, A::z };


Это сообщение отредактировал(а) Podarochek - 23.5.2008, 03:25
PM MAIL   Вверх
mes
Дата 23.5.2008, 03:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Podarochek @  23.5.2008,  03:24 Найти цитируемый пост)
error C2440: 'initializing' : cannot convert from 'void (__thiscall A::* )(void)' to 'void (__cdecl *)(void)'

компилятор не может преобразовать указатель_на_метод в указатель_на_статическую_функцию 
   void (**ftable)();   ->    void (**A::ftable)()


Это сообщение отредактировал(а) mes - 23.5.2008, 03:33


--------------------
PM MAIL WWW   Вверх
Podarochek
Дата 23.5.2008, 03:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 2.11.2007

Репутация: нет
Всего: нет



хм.. какой же нужно создать указатель?
Цитата(mes @  23.5.2008,  03:32 Найти цитируемый пост)
void (**ftable)();   ->    void (**A::ftable)()


PM MAIL   Вверх
mes
Дата 23.5.2008, 03:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Podarochek @  23.5.2008,  03:39 Найти цитируемый пост)
хм.. какой же нужно создать указатель?

Цитата(mes @  23.5.2008,  03:32 )
void (**ftable)();   ->    void (**A::ftable)()


сорри что ввел в заблуждение синтаксисом

подразумевалось что вместо 
void (**ftable)(); должна быть примерно  void (A::**ftable)()

а вобше имхо что  подобный способ реализации виртуальных функций выбран не совсем правильно


P.S еше и звездочки перепутал..помойму засиделся я слишком 

Это сообщение отредактировал(а) mes - 23.5.2008, 04:02


--------------------
PM MAIL WWW   Вверх
mes
Дата 23.5.2008, 05:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



вот набросал еше один вариант реализации  механизма виртуальных функций.

Код

#include <cstdlib>
#include <iostream>

using namespace std;

class A
{
 public:
    A() { A_vtable_init (); }
 public:
   void f1()              { A_vtable.f1(this); }
   void f2(int i)         { A_vtable.f2(this, i); }
   void f3(int i)         { A_vtable.f3(this, i); }

 private:
    void privf1() { cout << "  A::private_function()" ; }

 private:
   static void vf1(void* __this)  {
                                     cout << "A::f1() {";
                                     reinterpret_cast<A*>(__this)->privf1();
                                     cout <<"; };"<< endl; };

   static void vf2(void* __this, int i)  { cout << "A::f2(int)" << endl; };
   static void vf3(void* __this, int i)  { cout << "A::f3(int)" << endl; };

// VTABLE
   protected:
    class
    {
        public:
            void (*f1)(void* __this);
            void (*f2)(void* __this, int i);
            void (*f3)(void* __this, int i);
    } A_vtable;


    void A_vtable_init () {
                            A_vtable.f1=&A::vf1;
                            A_vtable.f2=&A::vf2;
                            A_vtable.f3=&A::vf3;
                          };

};

class B : public A
{
 public:
    B() { A_vtable_init (); }

 private:
    void privf1() { cout << "  B::private_function()" ; }
 private:
   static void vf1(void* __this)  {
                                     cout << "B::f1() {";
                                     reinterpret_cast<B*>(__this)->privf1();;
                                     cout <<"; };"<< endl; };
   static void vf2(void* __this, int i)  { cout << "B::f2(int)" << endl; };

   void A_vtable_init () {
                              A_vtable.f1=&B::vf1;
                              A_vtable.f2=&B::vf2;
                              A_vtable.f2=&B::vf2;
                          };

};


int main(int argc, char *argv[])
{
    A a;
    a.f1();
    a.f2(2);
    a.f3(6);

    cout <<endl;

    B b;
    b.f1();
    b.f2(3);
    b.f3(4);
    cout <<endl;

    A *pa=&b;
    pa->f1();
    pa->f2(4);
    pa->f3(5);

    cout <<endl;

    system("PAUSE");
    return EXIT_SUCCESS;
}



P.S 
Podarochek,  ты этой темой занялся из собственного любопытства или это задание?


--------------------
PM MAIL WWW   Вверх
mes
Дата 23.5.2008, 11:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(mes @  23.5.2008,  05:23 Найти цитируемый пост)
вот набросал еше один вариант реализации  механизма виртуальных функций.

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



--------------------
PM MAIL WWW   Вверх
mes
Дата 23.5.2008, 12:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



вот со статической vtable для каждого класса..

Код

#include <cstdlib>
#include <iostream>

using namespace std;


class A
{
   protected:
// vtable-type -------------------------------
    class A_vtable_t
    {
        public:
            A_vtable_t() {};

        public:
            void (*f1)(void* __this);
            void (*f2)(void* __this, int i);
            void (*f3)(void* __this, int i);
    };
// --------------------------------------------

  public:
    A() { A_vtable_set(A_vtable_get_for_A()); }

  public:
// pseudo-virtual-interface
    void f1()              { A_vtable_get()->f1(this); }
    void f2(int i)         { A_vtable_get()->f2(this, i); }
    void f3(int i)         { A_vtable_get()->f3(this, i); }

  private:
    void priv_fn() { cout << "  A::private_function()" ; }

  private:
// pseudo-virtual-implementation

        static void vf1(void* __this)  {
                                          cout << "A::f1() {";
                                          reinterpret_cast<A*>(__this)->priv_fn();
                                          cout <<"; };"<< endl;
                                        };

        static void vf2(void* __this, int i)  { cout << "A::f2(int);" << endl; };
        static void vf3(void* __this, int i)  { cout << "A::f3(int);" << endl; };



  public:
//vtable-initialization
        static void A_vtable_init () {
                                       A_vtable_get_for_A()->f1=&A::vf1;
                                       A_vtable_get_for_A()->f2=&A::vf2;
                                       A_vtable_get_for_A()->f3=&A::vf3;
                                     };

   protected:
//vtable-details
        static A_vtable_t *A_vtable_get_for_A()       { static A_vtable_t  A_vtable_for_A; return &A_vtable_for_A; }

        A_vtable_t *A_vtable_get()                    { return A_vtable_ptr; }
        A_vtable_t *A_vtable_set(A_vtable_t * vtable) { A_vtable_ptr = vtable; }

   private:
        A_vtable_t * A_vtable_ptr;

};

class B : public A
{
 public:
        B() { A_vtable_set(A_vtable_get_for_B()); }

 private:
        void priv_fn() { cout << "  B::private_function()" ; }

 private:
// pseudo-virtual-implementation
        static void vf1(void* __this)  {
                                          cout << "B::f1() {";
                                          reinterpret_cast<B*>(__this)->priv_fn();;
                                          cout <<"; };"<< endl;
                                       };
        static void vf2(void* __this, int i)   { cout << "B::f2(int);" << endl; };

        static A_vtable_t *A_vtable_get_for_B() { static A_vtable_t  A_vtable_for_B; return &A_vtable_for_B; }

  public:
//vtable-initialization
        static void A_vtable_init () {
                                       *A_vtable_get_for_B()=*A_vtable_get_for_A();
                                        A_vtable_get_for_B()->f1=&B::vf1;
                                        A_vtable_get_for_B()->f2=&B::vf2;
                                     };

};


int main(int argc, char *argv[])
{
    A::A_vtable_init ();
    B::A_vtable_init ();

    A a;
    a.f1();
    a.f2(2);
    a.f3(6);

    cout <<endl;

    B b;
    b.f1();
    b.f2(3);
    b.f3(4);
    cout <<endl;

    A *pa=&b;
    pa->f1();
    pa->f2(4);
    pa->f3(5);

    cout <<endl;

    system("PAUSE");
    return EXIT_SUCCESS;
}




--------------------
PM MAIL WWW   Вверх
xvr
Дата 23.5.2008, 13:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 60
Всего: 223



Цитата(Podarochek @ 23.5.2008,  02:37)
подскажите ошибку? smile 

Код

// Иллюстрация механизма виртуальных функций «классическим» Си
// Выделены компоненты, создаваемые транслятором
class A {
    void (**ftable)(); // Указатель на массив указателей
public: // виртуальных функций (таблицу функций)
    virtual void x(){}
    virtual void y(){}
    virtual void z(){}
    A();
    ~A(){} 
};
#define vx 0 // Индексы в массиве
#define vy 1 // указателей на виртуальные функции
#define vz 2 //
// Массив указателей функций класса А
void (*TableA[])() = { A::x, A::y, A::z };
A::A()

    ftable = TableA; /*...*/ // Назначение таблицы для класса А
}
class В : public A {
public:
    void x(){}
    void z(){}
    В();
    ~В(){}
};
// Массив адресов функций класса А в В
// А::у - наследуется из А, В::х - переопределяется в В
void (*TableB[])() = { В::x, A::y,В::z };
В::В()

    A::ftable = TableB; /*...*/ // Назначение таблицы для класса В
}
void main()
{
    
    В nnn; // Ссылается на объект производного класса В
    A *р = &nnn; // Указатель р базового класса А
    р->z();  // Реализация - (*(p->ftable[vz]))();
}



Ошибка в том, что это именно иллюстрация и не надо рассматривать это как программу на С++. Если надо сделать по этой иллюстрации реальную программу, то ее надо делать на С (ибо в С++ это все и так есть) из этого исходника, предварительно 'обработав его напильником' до состояния правильного С сорца.

PM MAIL   Вверх
mes
Дата 23.5.2008, 13:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Код

иллюстрация классическим СИ 

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



--------------------
PM MAIL WWW   Вверх
mes
Дата 23.5.2008, 13:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



подумав, все таки пришел к выводу, что иллюстрацию надо делать на С++,  без использования встроенных виртуальных функций
для демонстрации процесса "обратного"(callback) вызова нужной реализации потомка из базового класса.



--------------------
PM MAIL WWW   Вверх
Podarochek
Дата 23.5.2008, 15:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 2.11.2007

Репутация: нет
Всего: нет



Цитата(xvr @  23.5.2008,  13:29 Найти цитируемый пост)
void (**ftable)(); // Указатель на массив указателей

оператор поставил в protected
всеравно отстался вопрос:
Цитата(mes @  23.5.2008,  03:32 Найти цитируемый пост)
error C2440: 'initializing' : cannot convert from 'void (__thiscall A::* )(void)' to 'void (__cdecl *)(void)'

его реально решить в этой "иллюстрации"????



Цитата(xvr @  23.5.2008,  13:29 Найти цитируемый пост)
Ошибка в том, что это именно иллюстрация и не надо рассматривать это как программу на С++. Если надо сделать по этой иллюстрации реальную программу, то ее надо делать на С (ибо в С++ это все и так есть) из этого исходника, предварительно 'обработав его напильником' до состояния правильного С сорца.


стоит задача продемонстрировать механизм, никакой практической задачи. Как С++ никто не рассматривает об этом говорит постановка задачи .

Добавлено через 5 минут и 43 секунды
кстати..ссори что не по теме..подскажите что за ошибка:

Error    3    error LNK2001: unresolved external symbol "public: virtual double __thiscall Employee::earning(void)const " (?earning@Employee@@UBENXZ)    123.obj

PM MAIL   Вверх
mes
Дата 23.5.2008, 15:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Podarochek @  23.5.2008,  15:19 Найти цитируемый пост)
оператор поставил в protected
всеравно отстался вопрос:

Цитата(mes @  23.5.2008,  03:32 )
error C2440: 'initializing' : cannot convert from 'void (__thiscall A::* )(void)' to 'void (__cdecl *)(void)'


его реально решить в этой "иллюстрации"????


а при чем тут ограничение доступа (protected) ?
ты пытаешься привести метод к указателю на функцию
решений два :
1. заменить массив указателей на функции массивом указателей на нужный тип метода
или
2. запихивать в массив не методы, а статические функции (но они не смогут работать с членам класса)

Добавлено через 5 минут и 45 секунд
Цитата(Podarochek @  23.5.2008,  15:19 Найти цитируемый пост)
стоит задача продемонстрировать механизм, никакой практической задачи. Как С++ никто не рассматривает об этом говорит постановка задачи .

такой механизм можно продемонстрировать только в OOП (там где есть наследие) - так что классический Си отпадает 
Цитата(Podarochek @  23.5.2008,  15:19 Найти цитируемый пост)
кстати..ссори что не по теме..подскажите что за ошибка:

Error    3    error LNK2001: unresolved external symbol 

линкер не нашел тела функции

Добавлено через 14 минут и 43 секунды
Цитата(Podarochek @  23.5.2008,  15:19 Найти цитируемый пост)
его реально решить в этой "иллюстрации"????

а чем тебя не устраивают примеры приведенные мной? 
(хочу заметить что твой пример далеко не Сишний)

Это сообщение отредактировал(а) mes - 23.5.2008, 15:35


--------------------
PM MAIL WWW   Вверх
mes
Дата 23.5.2008, 15:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



а если реализовывать на Си то надо реализовывать классы и наследственность 


--------------------
PM MAIL WWW   Вверх
Podarochek
Дата 23.5.2008, 16:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 2.11.2007

Репутация: нет
Всего: нет



ссори, может я не так выразился...конечно же реализация будет предполагать С++, но имеется в виду раскрыть карты С++ и показать механизм явно (таблицу указателей). как сказал xvr  этот механизм и так есть уже в С++

Добавлено через 13 минут и 5 секунд
Я думаю по теме (еще одна ошибка) хоть убей, не могу понять что от меня хотят, кажись все реализованно как говорят по учебнику, а пишет:

Error    3    error C2259: 'Boss' : cannot instantiate abstract class    
Error    4    error C2259: 'CommissionWorker' : cannot instantiate abstract class    

и все...:(

Добавлено через 14 минут и 32 секунды
Код

#include <iostream> 
#include <cstring>
#include <cassert>
#include <iomanip>
using namespace std;; 


class Employee { 
public: 
    Employee( const char *, const char * ); 
     ~Employee () ; 
    const char *getFirstName() const; 
    const char *getLastName() const; 
 
//виртуальная функция образует абстрактный базовый класс Employee
    virtual double earning() const=0;//чистая виртуальная функция
    virtual void print() const;//виртуальная
private:
    char *firstName;
    char *lastName;
};

//Конструктор динамически выделяет память для
//имени и фамилии и использует strcpy для копирования
//имени и фамилии в объект
Employee::Employee( const char *first, const char *last )
{
    firstName = new char[ strlen( first) + 1 ]; 
    assert( firstName != 0 );//проверка выделения памяти
    strcpy( firstName, first); 
    lastName = new char[ strlen( last) + 1 ]; 
    assert( lastName != 0 ); // проверка выделения памяти
    strcpy( lastName, last); 
}

//деструктор освобождает динамически выделенную память
Employee::~Employee()
{
    delete [] firstName;//освобождение памяти
    delete [] lastName;//освобождение памяти
}

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

const char *Employee::getFirstName() const 

return firstName; //вызывающая программа должна осовободить память


//печать имени рабочего из класса Employee
void Employee::print() const 

    cout << firstName << " "<< lastName;
}

//класс Boss, производный от класса Employee 
class Boss :    public Employee
{
public: 
    Boss( const char *, const char *, double =0.0);
    void setWeeklySalary( double); 
    virtual double earnings() const; 
    virtual void print() const; 
private: 
    double weeklySalary; 
};

//определения функций-членов класса Boss

//конструктор класса Boss

Boss::Boss( const char *first, const char *last, double s ) 
        : Employee( first, last) //вызов конструктора базового класса
{
    setWeeklySalary ( s ); 


//установка еженедельного оклада для класса Boss
void Boss::setWeeklySalary( double s )
{
    weeklySalary = s > 0? s : 0; 

 
//начисление заработной платы в классе Boss
double Boss::earnings() const { return weeklySalary;} 

//печать имени служащего из класса Boss
void Boss::print() const 
{     
    cout << "\n    Administrator: "; 
    Employee::print (); 
}

//класс CommissionWorker, производный от класса Employee
class CommissionWorker : public Employee {
public: 
    CommissionWorker( const char *, const char *,double = 0.0, double = 0.0,int = 0 ); 
    void setSalary( double); 
    void setCommission( double); 
    void setQuantity( int); 
    virtual double earnings() const; 
    virtual void print() const; 
private: 
    double salary;//базовая заработная плата за неделю
    double commission;//комисионные от продажи одного изделия
    int quantity; //количество проданных изделий за неделю
}; 
//определение функций-членов класса CommissionWorker

//конструктор класса CommissionWorker
CommissionWorker::CommissionWorker(const char *first, const char *last, double s, double c, int q )
            : Employee( first, last) //конструктлор базового класса 
{
    setSalary( s );
    setCommission( c );
    setQuantity( q );
}

// установка еженедельного оклада для класса CommissionWorker 
void CommissionWorker::setSalary( double s) 
{
    salary = s > 0 ? s : 0; }

//установка комисионных для класса CommissionWorker 
void CommissionWorker::setCommission( double c ) 
{ commission = c > 0 ? c : 0; } 

//установка количества продаж изделий для класса CommissionWorker 
void CommissionWorker::setQuantity( int q ) 
{quantity =q > 0 ? q : 0; } 

//определение зароботной платы служащих из класса CommissionWorker 
double CommissionWorker::earnings() const 

    return  salary + commission * quantity; 


//Печать имени служащего из класса CommissionWorker 
void CommissionWorker::print() const    
{
    cout << "\nSlugaschij na komisionnux: ";
    Employee::print(); 
}

//создать вызов виртуальной функции ссылкой базового класса,
//используя динамическое связывание
void virtualViaPointer(const Employee* baseClassPtr)
{
    baseClassPtr->print();
    cout<<"zarabotal $"<<baseClassPtr->earning();
}

//создать вызов виртуальной функции ссылкой базового класса,
//используя динамическое связывание
void virtualViaReference(const Employee& baseClassRef)
{
    baseClassRef.print();
    cout<<"zarabotal $"<<baseClassRef.earning();
}
void main()
{
    Boss b("John","Smith", 800.00);
    b.print();//статическое связывание
    cout<<"zarabotal $"<<b.earnings();//статическое связывание

    virtualViaPointer(&b);//использует динамическое связывание
    virtualViaReference(b);//использует динамическое связывание

    CommissionWorker c("Sue", "Jones", 200.0, 3.0, 150);
    c.print();//статическое связывание
    cout<<"zarabotal $"<<c.earnings();//статическое связывание

    virtualViaPointer(&c);//использует динамическое связывание
    virtualViaReference(c);//использует динамическое связывание


}


PM MAIL   Вверх
mes
Дата 23.5.2008, 16:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Podarochek @  23.5.2008,  16:02 Найти цитируемый пост)
имеется в виду раскрыть карты С++ и показать механизм явно (таблицу указателей). как сказал xvr  этот механизм и так есть уже в С++ 


ну в принципе именно так я и понял задание с самого начала..потом сбили дополнительные высказывания ))

кстати насчет vtable - на самом деле таблица не представлена в виде набора  массива однотипных указетелей 
а виде набора разных структур, содержаших  разнотипные указатели

вот доработал еше чтоб больше походило на реальный механизм - хотя и еше есть что доработать:
Код

#include <cstdlib>
#include <iostream>

using namespace std;


class A
{
   protected:
// vtable-type -----------------------------------------------------------------
    struct A_vtable_t
    {
            A_vtable_t() {};

            void (*f1)(void* __this);
            void (*f2)(void* __this, int i);
            void (*f3)(void* __this, int i);
    };
//------------------------------------------------------------------------------

  public:
    A() { A_vtable_set(A_vtable_get_for_A()); }

  public:
// pseudo-virtual-interface
    void f1()              { A_vtable_get()->f1(this); }
    void f2(int i)         { A_vtable_get()->f2(this, i); }
    void f3(int i)         { A_vtable_get()->f3(this, i); }

  private:
    void priv_fn() { cout << "  A::private_function()" ; }

  private:
// pseudo-virtual-implementation
               void   vf1()       { cout << "A::f1() {";   priv_fn();  cout <<"; };"<< endl;  };
               void   vf2(int i)  { cout << "A::f2(int);" << endl; };
               void   vf3(int i)  { cout << "A::f3(int);" << endl; };


//vtable-details ---------------------------------------------------------------

// call_helper
  private:
        static void __vf1(void* __this)         { (reinterpret_cast<A*>(__this))->vf1();  };
        static void __vf2(void* __this, int i)  { (reinterpret_cast<A*>(__this))->vf2(i); };
        static void __vf3(void* __this, int i)  { (reinterpret_cast<A*>(__this))->vf3(i); };

  public:
//vtable-initialization
        static void A_vtable_init () {
                                       A_vtable_get_for_A()->f1=&A::__vf1;
                                       A_vtable_get_for_A()->f2=&A::__vf2;
                                       A_vtable_get_for_A()->f3=&A::__vf3;
                                     };

   protected:

        static A_vtable_t *A_vtable_get_for_A()       { static A_vtable_t  A_vtable_for_A; return &A_vtable_for_A; }

        A_vtable_t *A_vtable_get()                    { return A_vtable_ptr; }
        A_vtable_t *A_vtable_set(A_vtable_t * vtable) { A_vtable_ptr = vtable; }

   private:
        A_vtable_t * A_vtable_ptr;
//------------------------------------------------------------------------------

};

class B : public A
{
 public:
        B() { A_vtable_set(A_vtable_get_for_B()); }

 private:
        void priv_fn() { cout << "  B::private_function()" ; }

 private:
// pseudo-virtual-implementation
        void   vf1()       { cout << "B::f1() {"; priv_fn(); cout <<"; };"<< endl; };
        void   vf2(int i)  { cout << "B::f2(int);" << endl; };


//vtable-details ---------------------------------------------------------------
 private:
        static void __vf1(void* __this)         { (reinterpret_cast<B*>(__this))->vf1();  };
        static void __vf2(void* __this, int i)  { (reinterpret_cast<B*>(__this))->vf2(i); };

        static A_vtable_t *A_vtable_get_for_B() { static A_vtable_t  A_vtable_for_B; return &A_vtable_for_B; }

  public:
//vtable-initialization
        static void A_vtable_init () {
                                       *A_vtable_get_for_B()=*A_vtable_get_for_A();
                                        A_vtable_get_for_B()->f1=&B::__vf1;
                                        A_vtable_get_for_B()->f2=&B::__vf2;
                                     };
//------------------------------------------------------------------------------
};


int main(int argc, char *argv[])
{
    A::A_vtable_init ();
    B::A_vtable_init ();

    A a;
    a.f1();
    a.f2(2);
    a.f3(6);

    cout <<endl;

    B b;
    b.f1();
    b.f2(3);
    b.f3(4);
    cout <<endl;

    A *pa=&b;
    pa->f1();
    pa->f2(4);
    pa->f3(5);
    cout <<endl;

    system("PAUSE");
    return EXIT_SUCCESS;
}




Это сообщение отредактировал(а) mes - 23.5.2008, 16:29


--------------------
PM MAIL WWW   Вверх
Podarochek
Дата 23.5.2008, 16:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 2.11.2007

Репутация: нет
Всего: нет



спасибо mes буду разбираться
по поводу второго примера подскажешь?
PM MAIL   Вверх
mes
Дата 23.5.2008, 16:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Podarochek @  23.5.2008,  16:02 Найти цитируемый пост)
Error    3    error C2259: 'Boss' : cannot instantiate abstract class    
Error    4    error C2259: 'CommissionWorker' : cannot instantiate abstract class    


у тебя в этих классах есть абстрактные функции, а такие обжекты создавать нельзя

Код

class A
{
 public:
 virtual void abstract_fn() =0; //  абстрактная
};

class A1:public A
{
  public:
   virtual void abstract_fn() { .. } // реализация  - обьект такого типа создавть можно
};

// А * pa = new A; // нельзя 
А * pa = new A1; // создается А1, а интерфейс использоется от А


Добавлено через 1 минуту и 22 секунды
глянул в код - так у тебя у самого черным по белому написано
Код

    virtual double earning() const=0;//чистая виртуальная функция

 smile 


--------------------
PM MAIL WWW   Вверх
Podarochek
Дата 23.5.2008, 17:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 2.11.2007

Репутация: нет
Всего: нет



ну да базовый класс создан абстрактным, а производный класс например я изменил ->(теперь без virtual)
//класс CommissionWorker, производный от класса Employee
class CommissionWorker : public Employee {
public: 
    CommissionWorker( const char *, const char *,double = 0.0, double = 0.0,int = 0 ); 
    void setSalary( double); 
    void setCommission( double); 
    void setQuantity( int); 
    double earnings() const; 
    void print() const; 
private: 
    double salary;//базовая заработная плата за неделю
    double commission;//комисионные от продажи одного изделия
    int quantity; //количество проданных изделий за неделю
}; 

всеравно ошибки. Объекты производных классов разве нельзя создавать?

Добавлено через 4 минуты и 27 секунд
Цитата(mes @  23.5.2008,  16:41 Найти цитируемый пост)
class A
{
 public:
 virtual void abstract_fn() =0; //  абстрактная
};

class A1:public A
{
  public:
   virtual void abstract_fn() { .. } // реализация  - обьект такого типа создавть можно
};

// А * pa = new A; // нельзя 
А * pa = new A1; // создается А1, а интерфейс использоется от А

 
например объект А1 ob;
PM MAIL   Вверх
mes
Дата 23.5.2008, 17:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Podarochek @  23.5.2008,  17:07 Найти цитируемый пост)
например объект А1 ob; 

можно создавть объект и в стеке и по new при условие что у класса нет абстрактных методов..
пояснение: если у предка есть абстрактные методы - то они все должны получить реализацию у потомка

Цитата(Podarochek @  23.5.2008,  17:07 Найти цитируемый пост)
производный класс например я изменил ->(теперь без virtual)

слово virtual важно только для первого употребления, после этого у потомков метод с таким именем всегда будет считаться виртуальным
надо не интерфейс изменять а добавить реализцию к чистым методам

Добавлено через 1 минуту и 8 секунд
P.S зря ты обе темы в одной ветке запостил .. путает 


--------------------
PM MAIL WWW   Вверх
Podarochek
Дата 23.5.2008, 17:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 2.11.2007

Репутация: нет
Всего: нет



smile 

earnings вместо earning!!! Я балбес. Конечно они должны быть одинаковы...откуда взяласт s до сих пор не могу понять smile

Добавлено через 2 минуты и 3 секунды
о втором посте(т.е. ответвлении от основной темы) забыли...прошу прощения!!! smile 
PM MAIL   Вверх
mes
Дата 24.5.2008, 15:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Вот еше раз доработал код, 
на этот наконец то отделил технические операции от представления класса : 
этот вариант можно считать законченым  (потому как для дальнейших улучшений требуются шаблоны, а это будет вне задания ) 
 smile 

Код

#include <cstdlib>
#include <iostream>

using namespace std;


namespace A_vtable
{
// vtable-type -----------------------------------------------------------------
    struct type
    {
            void (*f1)(void* __this);
            void (*f2)(void* __this, int i);
            void (*f3)(void* __this, int i);
    };
    static A_vtable::type *get_for_A() { static type  vtable; return &vtable; }
    static A_vtable::type *get_for_B() { static type  vtable; return &vtable; }
//------------------------------------------------------------------------------
};

class A
{
    friend class __A;
  public:
    A() { A_vtable_set(A_vtable::get_for_A()); }

  private:
    void priv_fn() { cout << "  A::private_function()" ; }

  private:
// pseudo-virtual-implementation
               void   vf1()       { cout << "A::f1() {";   priv_fn();  cout <<"; };"<< endl;  };
               void   vf2(int i)  { cout << "A::f2(int);" << endl; };
               void   vf3(int i)  { cout << "A::f3(int);" << endl; };

  public:
// pseudo-virtual-interface ----------------------------------------------------
    void f1()              { A_vtable_get()->f1(this); }
    void f2(int i)         { A_vtable_get()->f2(this, i); }
    void f3(int i)         { A_vtable_get()->f3(this, i); }

   public:
        A_vtable::type *A_vtable_get()                        { return A_vtable_ptr; }
        A_vtable::type *A_vtable_set(A_vtable::type * vtable) { A_vtable_ptr = vtable; }

   private:
        A_vtable::type * A_vtable_ptr;
//------------------------------------------------------------------------------
};

class B : public A
{
        friend class __B;
 public:
        B() {  A_vtable_set(A_vtable::get_for_B());  }

 private:
        void priv_fn() { cout << "  B::private_function()" ; }

 private:
// pseudo-virtual-implementation
        void   vf1()       { cout << "B::f1() {"; priv_fn(); cout <<"; };"<< endl; };
        void   vf2(int i)  { cout << "B::f2(int);" << endl; };
};

// Helpers ----------------------------------------------------------------------
// обслуживание виртуального механизма:
class __A
{
  private:
        static void __vf1(void* __this)         { (reinterpret_cast<A*>(__this))->vf1();  };
        static void __vf2(void* __this, int i)  { (reinterpret_cast<A*>(__this))->vf2(i); };
        static void __vf3(void* __this, int i)  { (reinterpret_cast<A*>(__this))->vf3(i); };

  public:
        static void __init () {
                                 A_vtable::get_for_A()->f1=&__A::__vf1;
                                 A_vtable::get_for_A()->f2=&__A::__vf2;
                                 A_vtable::get_for_A()->f3=&__A::__vf3;
                              };
};

class __B
{
  private:
        static void __vf1(void* __this)         { (reinterpret_cast<B*>(__this))->vf1();  };
        static void __vf2(void* __this, int i)  { (reinterpret_cast<B*>(__this))->vf2(i); };

  public:
        static void __init () {
                                       *A_vtable::get_for_B()=*A_vtable::get_for_A();
                                        A_vtable::get_for_B()->f1=&__B::__vf1;
                                        A_vtable::get_for_B()->f2=&__B::__vf2;
                              };
};
//------------------------------------------------------------------------------

int main(int argc, char *argv[])
{

    __A::__init ();
    __B::__init ();

    A a;
    a.f1();
    a.f2(2);
    a.f3(6);

    cout <<endl;

    B b;
    b.f1();
    b.f2(3);
    b.f3(4);
    cout <<endl;

    A *pa= new B();
    pa->f1();
    pa->f2(4);
    pa->f3(5);

    delete pa;

    cout <<endl;

    system("PAUSE");
    return EXIT_SUCCESS;
}




--------------------
PM MAIL WWW   Вверх
mes
Дата 24.5.2008, 18:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(mes @  24.5.2008,  15:42 Найти цитируемый пост)
этот вариант можно считать законченым 

мелкие доработки 
перегрузка  некоторых функций (возврашаюших vtable)  по типу
наследование прокси-функций 
а также  убрал зависимость от порядка инициализации 

Код

#include <cstdlib>
#include <iostream>

using namespace std;


class A;
class B;

namespace A_vtable
{
// vtable-type -----------------------------------------------------------------
//
    struct type
    {
            void (*f1)(void* __this);
            void (*f2)(void* __this, int i);
            void (*f3)(void* __this, int i);

    };
    static A_vtable::type *get(A*) { static type vtable; return &vtable; }
    static A_vtable::type *get(B*) { static type vtable; return &vtable; }

    class ptr
    {
      public:
        type * get()                     { return p_vtable;   }
        void set (type * vtable)         { p_vtable = vtable; }
        operator type*()                 { return get();      }
        void operator () (type * vtable) { set(vtable);       }
      private:
        A_vtable::type * p_vtable;
     };
//------------------------------------------------------------------------------
};

class A
{
    friend class __A;
  public:
    A() { A_vtable_ptr(A_vtable::get(this)); }

  private:
    void priv_fn() { cout << "  A::private_function()" ; }

  private:
// pseudo-virtual-implementation
// отсутствует в абстракном классе
               void   vf1()       { cout << "A::f1() {";   priv_fn();  cout <<"; };"<< endl;  };
               void   vf2(int i)  { cout << "A::f2(int);" << endl; };
               void   vf3(int i)  { cout << "A::f3(int);" << endl; };

  public:
// pseudo-virtual-interface ----------------------------------------------------
    void f1()              { (*A_vtable_ptr).f1(this); }
    void f2(int i)         { (*A_vtable_ptr).f2(this, i); }
    void f3(int i)         { (*A_vtable_ptr).f3(this, i); }

   protected:
        A_vtable::ptr A_vtable_ptr;
//------------------------------------------------------------------------------
};

class B : public A
{
        friend class __B;
 public:
        B() {  A_vtable_ptr(A_vtable::get(this));  }

 private:
        void priv_fn() { cout << "  B::private_function()" ; }

 private:
// pseudo-virtual-implementation
        void   vf1()       { cout << "B::f1() {"; priv_fn(); cout <<"; };"<< endl; };
        void   vf2(int i)  { cout << "B::f2(int);" << endl; };
};

// Helpers ----------------------------------------------------------------------
// обслуживание виртуального механизма:
class __A
{
  protected:
        static void __vf1(void* __this)         { (reinterpret_cast<A*>(__this))->vf1();  };
        static void __vf2(void* __this, int i)  { (reinterpret_cast<A*>(__this))->vf2(i); };
        static void __vf3(void* __this, int i)  { (reinterpret_cast<A*>(__this))->vf3(i); };

  public:
        static void __init () {
                                 A_vtable::get((A*)NULL)->f1=&__A::__vf1;
                                 A_vtable::get((A*)NULL)->f2=&__A::__vf2;
                                 A_vtable::get((A*)NULL)->f3=&__A::__vf3;
                              };
};

class __B: protected __A
{
  private:
        static void __vf1(void* __this)         { (reinterpret_cast<B*>(__this))->vf1();  };
        static void __vf2(void* __this, int i)  { (reinterpret_cast<B*>(__this))->vf2(i); };

  public:
        static void __init () {
                                        A_vtable::get((B*)NULL)->f1=&__B::__vf1;
                                        A_vtable::get((B*)NULL)->f2=&__B::__vf2;
                                        A_vtable::get((B*)NULL)->f3=&__B::__vf3;
                              };
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

int main(int argc, char *argv[])
{
    __A::__init ();
    __B::__init ();

    A a;
    a.f1();
    a.f2(2);
    a.f3(6);

    cout <<endl;

    B b;
    b.f1();
    b.f2(3);
    b.f3(4);
    cout <<endl;

    A *pa= new B();
    pa->f1();
    pa->f2(4);
    pa->f3(5);

    delete pa;

    cout <<endl;

    system("PAUSE");
    return EXIT_SUCCESS;
}



Это сообщение отредактировал(а) mes - 24.5.2008, 19:04


--------------------
PM MAIL WWW   Вверх
mes
Дата 24.5.2008, 20:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Podarochek, не думал, что меня так зацепит эта тема ))
вот еше немного добавил в илюстрацию 
теперь есть:
псевдо pure-абстрактный класс,
и два класса: один с частичной другой с полной перегрузкой абстрактных методов
Код

#include <cstdlib>
#include <iostream>

using namespace std;


class A;
class B;
class C;

namespace A_vtable
{
// vtable-type -----------------------------------------------------------------
//
    struct type
    {
        void (*f1)(void* __this);
        void (*f2)(void* __this, int i);
        void (*f3)(void* __this, int i);
    };

// ниже нужен шаблон вместо повторения
    static A_vtable::type *get(A*) { static type vtable; return &vtable; }
    static A_vtable::type *get(B*) { static type vtable; return &vtable; }
    static A_vtable::type *get(C*) { static type vtable; return &vtable; }

    class ptr
    {
      public:
        type * get()                     { return p_vtable;   }
        operator type*()                 { return get();      }
        type * operator ->()             { return get();      }

        void set (type * vtable)         { p_vtable = vtable; }
        void operator () (type * vtable) { set(vtable);       }

      private:
        A_vtable::type * p_vtable;
     };
//------------------------------------------------------------------------------
};

class A // абстрактный
{
        friend class __A;
  public:
        A() { A_vtable_ptr(A_vtable::get(this)); }

  private:
        void priv_fn() { cout << "  A::private_function()" ; }

  private:
// pseudo-virtual-implementation
// отсутствует в абстракном классе

  public:
// pseudo-virtual-interface -
        void f1()      { (*A_vtable_ptr).f1(this); }
        void f2(int i) { (*A_vtable_ptr).f2(this, i); }
        void f3(int i) { (*A_vtable_ptr).f3(this, i); }

   protected:
        A_vtable::ptr A_vtable_ptr;
};

class B: public A
{
        friend class __B;
  public:
        B() { A_vtable_ptr(A_vtable::get(this)); }

  private:
        void priv_fn() { cout << "  B::private_function()" ; }

  private:
// pseudo-virtual-implementation
        void vf1()       { cout << "B::f1();" << endl; };
        void vf2(int i)  { cout << "B::f2(int) {";   priv_fn();  cout <<"; };"<< endl;  };
        void vf3(int i)  { cout << "B::f3(int);" << endl; };

};

class C : public B
{
        friend class __C;
 public:
        C() {  A_vtable_ptr(A_vtable::get(this));  }

 private:
        void priv_fn() { cout << "  C::private_function()" ; }

 private:
// pseudo-virtual-implementation
        void vf1()       { cout << "C::f1();" << endl; };
        void vf2(int i)  { cout << "C::f2(int);" << endl; };
        void vf3(int i)  { cout << "C::f3(int) {"; priv_fn(); cout <<"; };"<< endl; };
};

// Helpers ----------------------------------------------------------------------
// обслуживание виртуального механизма:

// ниже использован #define - но должен быть шаблон
#define A_VTABLE_INIT_PROC(Name, Class) public: static void Name() { \
                                 A_vtable::get((Class*)NULL)->f1=&__##Class::__vf1; \
                                 A_vtable::get((Class*)NULL)->f2=&__##Class::__vf2; \
                                 A_vtable::get((Class*)NULL)->f3=&__##Class::__vf3; };

class __A
{
  protected:
        static void __on_abstract_call () { cout << "exception:: abstract method" << endl;  }

  protected:
        static void __vf1(void* __this)         { __on_abstract_call (); };
        static void __vf2(void* __this, int i)  { __on_abstract_call (); };
        static void __vf3(void* __this, int i)  { __on_abstract_call (); };

        A_VTABLE_INIT_PROC(__init,A)

};

class __B: protected __A
{
  private:
        static void __vf1(void* __this)         { (reinterpret_cast<B*>(__this))->vf1();  };
        static void __vf2(void* __this, int i)  { (reinterpret_cast<B*>(__this))->vf2(i); };

        A_VTABLE_INIT_PROC(__init,B)
};

class __C: protected __B
{
  private:
        static void __vf1(void* __this)         { (reinterpret_cast<C*>(__this))->vf1();  };
        static void __vf2(void* __this, int i)  { (reinterpret_cast<C*>(__this))->vf2(i); };
        static void __vf3(void* __this, int i)  { (reinterpret_cast<C*>(__this))->vf3(i); };

        A_VTABLE_INIT_PROC(__init,C)

};
#undef A_VTABLE_INIT_PROC
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

void test_virtual (A* pa)
{
    pa->f1();
    pa->f2(4);
    pa->f3(5);
};

int main(int argc, char *argv[])
{
    __A::__init ();
    __B::__init ();
    __C::__init ();

    cout <<endl;

    A *pa;

    pa = new A();
      test_virtual(pa);
    delete pa;

    cout <<endl;

    pa = new B();
      test_virtual(pa);
    delete pa;

    cout <<endl;

    pa = new C();
      test_virtual(pa);
    delete pa;

    cout <<endl;

    system("PAUSE");
    return EXIT_SUCCESS;
}


Это сообщение отредактировал(а) mes - 24.5.2008, 20:39


--------------------
PM MAIL WWW   Вверх
mes
Дата 24.5.2008, 23:00 (ссылка) |   (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Ну вот дошли и до извращений .
Специально для любителей таких вещей :

main.cpp :
Код

#include <cstdlib>
#include <iostream>

using namespace std;

#define VIRTUAL_ILLUSTRATION

#ifdef VIRTUAL_ILLUSTRATION

#define PASSED_PROMPT(instruction) /* instruction */
#define SPECIFIC_NAME(Name) interior_name_##Name
#define INTERIOR(a) a

#else

#define PASSED_PROMPT(instruction)  instruction
#define SPECIFIC_NAME(Name) Name
#define INTERIOR(a) /* a */

#endif

// PASSED_PROMPT     - показывает какая инструкция должна быть в реальном случае
// SPECIFIC_NAME     - внутреннее имя используемое компилятором
// INTERIOR(a)       - иллюстрация внутреннего компиляторного кода


INTERIOR( void SPECIFIC_NAME(on_abstract_call) () { cout << "exception:: abstract method" << endl;  } )

//------------------------------------------------------------------------------
// предопределения построенные компилятором для виртуальной таблицы A_vtable

class A;
class B;
class C;

INTERIOR
(           namespace A_vtable
            {  struct type;

               type *get(A*);
               type *get(B*);
               type *get(C*);

              class ptr
              {
                  public:
                    type * get()                     { return p_vtable;   }
                    operator type*()                 { return get();      }
                    type * operator ->()             { return get();      }

                    void set (type * vtable)         { p_vtable = vtable; }
                    void operator () (type * vtable) { set(vtable);       }

                  private:
                    A_vtable::type * p_vtable;
               };
            };
)
//------------------------------------------------------------------------------

class A // абстрактный
{
        friend class __A;
  public:
        A() { INTERIOR( init_virtual(); ) }

  private:
        void priv_fn() { cout << "  A::private_function()" ; }

  private:

// virtual implementation отсутствует в pure abstract классе

  public:

    PASSED_PROMPT( virtual ) void f1()      PASSED_PROMPT( =0 );
    PASSED_PROMPT( virtual ) void f2(int i) PASSED_PROMPT( =0 );
    PASSED_PROMPT( virtual ) void f3(int i) PASSED_PROMPT( =0 );

    INTERIOR(  protected: A_vtable::ptr A_vtable_ptr; )
    INTERIOR(  private: void init_virtual (); )
};

//------------------------------------------------------------------------------

class B: public A
{
        friend class __B;
  public:
        B() { INTERIOR( init_virtual(); )}

  private:
        void priv_fn() { cout << "  B::private_function()" ; }

  private:
       void SPECIFIC_NAME(f1) ()       { cout << "B::f1();" << endl; };
       void SPECIFIC_NAME(f2) (int i)  { cout << "B::f2(int) {";   priv_fn();  cout <<"; };"<< endl;  };
//       void SPECIFIC_NAME(f3) (int i)  { cout << "B::f3(int);" << endl; };

       INTERIOR(  private: void init_virtual (); )
};

//------------------------------------------------------------------------------

class C : public B
{
        friend class __C;
   public:
        C() {  INTERIOR( init_virtual(); ) }

   private:
        void priv_fn() { cout << "  C::private_function()" ; }

   private:
       void SPECIFIC_NAME(f1) ()       { cout << "C::f1();" << endl; };
       void SPECIFIC_NAME(f2) (int i)  { cout << "C::f2(int);" << endl; };
       void SPECIFIC_NAME(f3) (int i)  { cout << "C::f3(int) {"; priv_fn(); cout <<"; };"<< endl; };

       INTERIOR(  private: void init_virtual (); )
};

//------------------------------------------------------------------------------
#ifdef VIRTUAL_ILLUSTRATION

#include "A_vtable.h" // INTERIOR

#endif
//------------------------------------------------------------------------------

void test_virtual (A* pa)
{
    pa->f1();
    pa->f2(4);
    pa->f3(5);
};

int main(int argc, char *argv[])
{
INTERIOR( __A::__init (); );
INTERIOR( __B::__init (); );
INTERIOR( __C::__init (); );

    cout <<endl;

    A *pa;

#ifdef VIRTUAL_ILLUSTRATION
    pa = new A();
    cout << "new A" << endl;

      test_virtual(pa);

    cout << "delete A" << endl;
    delete pa;
#else
    cout << "Can't create A because it has abstract method " << endl;
#endif

    cout <<endl;

#ifdef VIRTUAL_ILLUSTRATION
    pa = new B();
    cout << "new B" << endl;

      test_virtual(pa);

    cout << "delete B" << endl;
    delete pa;
#else
    cout << "Can't create B because it has abstract method " << endl;
#endif

    cout <<endl;

    pa = new C();
    cout << "new C;" << endl;

      test_virtual(pa);

    cout << "delete C" << endl;
    delete pa;

    cout <<endl;

    system("PAUSE");
    return EXIT_SUCCESS;
}


A_vtable.h :
Код


// обслуживание виртуального механизма: ----------------------------------------

#define A_VTABLE_INIT_PROC(Name, Class) public: static void Name() { \
                                 A_vtable::get((Class*)NULL)->f1=&__##Class::SPECIFIC_NAME(f1); \
                                 A_vtable::get((Class*)NULL)->f2=&__##Class::SPECIFIC_NAME(f2); \
                                 A_vtable::get((Class*)NULL)->f3=&__##Class::SPECIFIC_NAME(f3); };



namespace A_vtable
{
    struct type
    {
        void (*f1)(void* __this);
        void (*f2)(void* __this, int i);
        void (*f3)(void* __this, int i);
    };
};

// ниже использован #define - но должен быть шаблон


extern void SPECIFIC_NAME(on_abstract_call) ();
class __A
{
  protected:


  protected:
        static void SPECIFIC_NAME(f1)(void* __this)         { SPECIFIC_NAME (on_abstract_call)(); };
        static void SPECIFIC_NAME(f2)(void* __this, int i)  { SPECIFIC_NAME (on_abstract_call)(); };
        static void SPECIFIC_NAME(f3)(void* __this, int i)  { SPECIFIC_NAME (on_abstract_call)(); };

        A_VTABLE_INIT_PROC(__init,A)

};

class __B: protected __A
{
  private:
        static void SPECIFIC_NAME(f1)(void* __this)         { (reinterpret_cast<B*>(__this))->SPECIFIC_NAME(f1)();  };
        static void SPECIFIC_NAME(f2)(void* __this, int i)  { (reinterpret_cast<B*>(__this))->SPECIFIC_NAME(f2)(i); };

        A_VTABLE_INIT_PROC(__init,B)
};

class __C: protected __B
{
  private:
        static void SPECIFIC_NAME(f1)(void* __this)         { (reinterpret_cast<C*>(__this))->SPECIFIC_NAME(f1)();  };
        static void SPECIFIC_NAME(f2)(void* __this, int i)  { (reinterpret_cast<C*>(__this))->SPECIFIC_NAME(f2)(i); };
        static void SPECIFIC_NAME(f3)(void* __this, int i)  { (reinterpret_cast<C*>(__this))->SPECIFIC_NAME(f3)(i); };

        A_VTABLE_INIT_PROC(__init,C)

};
#undef A_VTABLE_INIT_PROC

//------------------------------------------------------------------------------

void A::f1()      { (*A_vtable_ptr).f1(this); }
void A::f2(int i) { (*A_vtable_ptr).f2(this, i); }
void A::f3(int i) { (*A_vtable_ptr).f3(this, i); }

void A::init_virtual() { A_vtable_ptr(A_vtable::get(this)); }
void B::init_virtual() { A_vtable_ptr(A_vtable::get(this)); }
void C::init_virtual() { A_vtable_ptr(A_vtable::get(this)); }

A_vtable::type *A_vtable::get(A*) { static type vtable; return &vtable; }
A_vtable::type *A_vtable::get(B*) { static type vtable; return &vtable; }
A_vtable::type *A_vtable::get(C*) { static type vtable; return &vtable; }

//------------------------------------------------------------------------------



теперь  есть возможность "переключать" код между режимами (за/раскомментировать строку #define VIRTUAL_ILLUSTRATION ) :
1. реальные виртуальные методы
2. псевдо виртуальные методы (реализованые вручную (без помощи)  компилятора )

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






Это сообщение отредактировал(а) mes - 24.5.2008, 23:08


--------------------
PM MAIL WWW   Вверх
xvr
Дата 25.5.2008, 10:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 60
Всего: 223



Цитата(mes @ 24.5.2008,  23:00)
Ну вот дошли и до извращений .
Специально для любителей таких вещей :

Круто  smile С реальной реализацией в компиляторе есть только одно отличие - компилятор строит таблицы виртуальных функций статически (без метода __init) и располагает их каждую в своем классе (а не все в самом базовом).
Предлагаю в качестве дальнейших шагов рассмотреть множественное наследование, виртуальные базовые классы и RTTI/dynamic_cast  smile 

PS. Предлагаю предыдущий пост поместить в FAQ.


Это сообщение отредактировал(а) xvr - 25.5.2008, 10:36
PM MAIL   Вверх
mes
Дата 25.5.2008, 12:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(xvr @  25.5.2008,  10:32 Найти цитируемый пост)
компилятор строит таблицы виртуальных функций статически (без метода __init) 

учел замечания и убрал лишнее. буду рад комментам 
Код


#include <cstdlib>
#include <iostream>

#define VIRTUAL_ILLUSTRATION

#ifdef VIRTUAL_ILLUSTRATION

#define ___V(instruction) /* instruction */
#define ___(Name) interior_name_##Name
#define ___INTERIOR_V___(a) a

#else

#define ___V(instruction)  instruction
#define ___(Name) Name
#define ___INTERIOR_V___(a) /* a */

#endif

// ___V     - показывает какая инструкция должна быть в реальном случае
// ___     - внутреннее имя используемое компилятором
// ___INTERIOR_V___(a)       - иллюстрация внутреннего компиляторного кода

___INTERIOR_V___( void ___(on_abstract_call) () { std::cout << "exception:: abstract method" << std::endl;  } )

////////////////////////////////////////////////////////////////////////////////
// общие конструкции для виртуального механизма --------------------------------

#define DEFINE_VTABLE_PTR_WRAPPER(Class, type )     class Class \
               {                                                               \
                  public:                                                      \
                    type * get()                     { return p_vtable;   }    \
                    operator type*()                 { return get();      }    \
                    type * operator ->()             { return get();      }    \
                    void set (type * vtable)         { p_vtable = vtable; }    \
                    void operator () (type * vtable) { set(vtable);       }    \
                  private:                                                     \
                    type * p_vtable;                                           \
               };
//------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////
// базовая конструкция виртуального механизма класса А (и его потомков): -------

#define DEFINE_CONCRETE_A_VTABLE(Class)   private: \
               struct ___(A_vtable_concrete): public ___(A_vtable)::type     \
               { ___(A_vtable_concrete())                                              \
                                    { f1=&Class::___(f1);                              \
                                      f2=&Class::___(f2);                              \
                                      f3=&Class::___(f3);                              \
                                    };                                                           \
               };                                                                                \
               public: static ___(A_vtable_concrete)* ___(GetConcrete_A_vtable) () \
                                            { static ___(A_vtable_concrete) vtable; return &vtable; }

  ___INTERIOR_V___
  (
         namespace ___(A_vtable)
         {
               struct type
               {
                    void (*f1)(void* __this);
                    void (*f2)(void* __this, int i);
                    void (*f3)(void* __this, int i);
               };

               DEFINE_VTABLE_PTR_WRAPPER(ptr, type);
          };
  )
//------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////


class A // абстрактный
{
  public:
        A() { ___INTERIOR_V___( ___(init_vtable_ptr)(); ) }

  public:

    ___V( virtual ) void f1()      ___V( =0 );
    ___V( virtual ) void f2(int i) ___V( =0 );
    ___V( virtual ) void f3(int i) ___V( =0 );

  private:
        void priv_fn() { std::cout << "  A::private_function()" ; }

  private:

// virtual implementation отсутствует в pure abstract классе

  ___INTERIOR_V___
  (

      protected:
            static void ___(f1)(void* __this)         { ___(on_abstract_call)(); };
            static void ___(f2)(void* __this, int i)  { ___(on_abstract_call)(); };
            static void ___(f3)(void* __this, int i)  { ___(on_abstract_call)(); };

      protected:
            ___(A_vtable)::ptr ___(A_vtable_ptr);
      private:
            void ___(init_vtable_ptr) ();

            DEFINE_CONCRETE_A_VTABLE(A)
  )
};

//------------------------------------------------------------------------------

class B: public A
{
  public:
        B() { ___INTERIOR_V___( ___(init_vtable_ptr)(); )}

  private:
        void priv_fn() { std::cout << "  B::private_function()" ; }

  private:
       void ___(f1) ()       { std::cout << "B::f1();" << std::endl; };
       void ___(f2) (int i)  { std::cout << "B::f2(int) {";   priv_fn();  std::cout <<"; };"<< std::endl;  };
//     void ___(f3) (int i)  { std::cout << "B::f3(int);" << std::endl; }; // не перегруженая виртуальная функция


  ___INTERIOR_V___
  (

      protected:
        static void ___(f1)(void* __this)         { (reinterpret_cast<B*>(__this))->___(f1)();  };
        static void ___(f2)(void* __this, int i)  { (reinterpret_cast<B*>(__this))->___(f2)(i); };
//      static void ___(f3)(void* __this, int i)  { (reinterpret_cast<B*>(__this))->___(f3)(i); };

      private:
            void ___(init_vtable_ptr) ();

            DEFINE_CONCRETE_A_VTABLE(B)
  )

};

//------------------------------------------------------------------------------

class C : public B
{
   public:
        C() {  ___INTERIOR_V___( ___(init_vtable_ptr)(); ) }

   private:
        void priv_fn() { std::cout << "  C::private_function()" ; }

   private:
       void ___(f1) ()       { std::cout << "C::f1();" << std::endl; };
       void ___(f2) (int i)  { std::cout << "C::f2(int);" << std::endl; };
       void ___(f3) (int i)  { std::cout << "C::f3(int) {"; priv_fn(); std::cout <<"; };"<< std::endl; };

  ___INTERIOR_V___
  (

      protected:
        static void ___(f1)(void* __this)         { (reinterpret_cast<C*>(__this))->___(f1)();  };
        static void ___(f2)(void* __this, int i)  { (reinterpret_cast<C*>(__this))->___(f2)(i); };
        static void ___(f3)(void* __this, int i)  { (reinterpret_cast<C*>(__this))->___(f3)(i); };

      private:
            void ___(init_vtable_ptr) ();

            DEFINE_CONCRETE_A_VTABLE(C)
  )
};

////////////////////////////////////////////////////////////////////////////////
// связывание виртуального механизма : -----------------------------------------
  ___INTERIOR_V___
  (
        void A::f1()      { ___(A_vtable_ptr) -> f1(this); }
        void A::f2(int i) { ___(A_vtable_ptr) -> f2(this, i); }
        void A::f3(int i) { ___(A_vtable_ptr) -> f3(this, i); }

        void A::___(init_vtable_ptr)() { ___(A_vtable_ptr) .set( ___(GetConcrete_A_vtable)() ); }
        void B::___(init_vtable_ptr)() { ___(A_vtable_ptr) .set( ___(GetConcrete_A_vtable)() ); }
        void C::___(init_vtable_ptr)() { ___(A_vtable_ptr) .set( ___(GetConcrete_A_vtable)() ); }

   )
//------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////

void test_virtual (A* pa)
{
    pa->f1();
    pa->f2(4);
    pa->f3(5);
};
////////////////////////////////////////////////////////////////////////////////
// MAIN ------------------------------------------------------------------------

int main(int argc, char *argv[])
{

#ifdef VIRTUAL_ILLUSTRATION
    std::cout << "/* illustration-mode: */" <<std::endl;
#else
    std::cout << "/* real-mode: */" <<std::endl;
#endif

    std::cout <<std::endl;

    A *pa;

#ifdef VIRTUAL_ILLUSTRATION
    pa = new A();
    std::cout << "new A" << std::endl;

      test_virtual(pa);

    std::cout << "delete A" << std::endl;
    delete pa;
#else
    std::cout << "Can't create A because it has abstract method " << std::endl;
#endif

    std::cout <<std::endl;

#ifdef VIRTUAL_ILLUSTRATION
    pa = new B();
    std::cout << "new B" << std::endl;

      test_virtual(pa);

    std::cout << "delete B" << std::endl;
    delete pa;
#else
    std::cout << "Can't create B because it has abstract method " << std::endl;
#endif

    std::cout <<std::endl;

    pa = new C();
    std::cout << "new C" << std::endl;

      test_virtual(pa);

    std::cout << "delete C" << std::endl;
    delete pa;

    std::cout <<std::endl;

    system("PAUSE");
    return EXIT_SUCCESS;
}

//------------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////





Цитата(xvr @  25.5.2008,  10:32 Найти цитируемый пост)
Предлагаю в качестве дальнейших шагов рассмотреть множественное наследование, виртуальные базовые классы и RTTI/dynamic_cast 

надо будет подумать ( когда будет свободное время )

Это сообщение отредактировал(а) mes - 25.5.2008, 15:56


--------------------
PM MAIL WWW   Вверх
xvr
Дата 25.5.2008, 20:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 60
Всего: 223



Цитата(mes @ 25.5.2008,  12:16)
Цитата(xvr @  25.5.2008,  10:32 Найти цитируемый пост)
компилятор строит таблицы виртуальных функций статически (без метода __init) 

учел замечания и убрал лишнее. буду рад комментам 

Коментарий: способ оформления таблицы виртуальных функций (как это делает компилятор)
Код

struct vtable_type
{
  void (*f1)(void* __this);
  void (*f2)(void* __this, int i);
  void (*f3)(void* __this, int i);
};

class A // абстрактный
{
  public:
        A();

  public:

    ___V( virtual ) void f1()      ___V( =0 );
    ___V( virtual ) void f2(int i) ___V( =0 );
    ___V( virtual ) void f3(int i) ___V( =0 );

  private:
        void priv_fn() { std::cout << "  A::private_function()" ; }

  private:

// virtual implementation отсутствует в pure abstract классе

  ___INTERIOR_V___
  (

      protected:
            static void ___(f1)(void* __this)         { ___(on_abstract_call)(); };
            static void ___(f2)(void* __this, int i)  { ___(on_abstract_call)(); };
            static void ___(f3)(void* __this, int i)  { ___(on_abstract_call)(); };

      protected:
            ___(A_vtable)::ptr ___(A_vtable_ptr);
  )
};

static vtable_type vtable_classA = {&A::___(f1),&A::___(f2),&A::___(f3)};

A::A() {A_vtable_ptr=&vtable_classA;}


т.е. какой либо код для построения таблицы виртуальных функций отсуствует вообще, только присваивание адреса в конструкторе

PM MAIL   Вверх
mes
Дата 25.5.2008, 20:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(xvr @  25.5.2008,  20:12 Найти цитируемый пост)
т.е. какой либо код для построения таблицы виртуальных функций отсуствует вообще, только присваивание адреса в конструкторе


то есть разница что компилятор строит ее в компил-тайм, а в моем примере в рантайме ?
ну так оно и есть:
в одном случае таблица строится управляемая инструкциями типа virtual во время парсинга кода
во втором строится за счет рант-тайм кода конструкторов

также наверно то что используются прокси функции для вызова ?
ну так в реальном (откомпиляторном виде) все функции приведены к такому виду и компилятору не приходится мучаться
а нам приходится его обманывать ))
 smile 
 

 


--------------------
PM MAIL WWW   Вверх
Podarochek
Дата 26.5.2008, 09:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 2.11.2007

Репутация: нет
Всего: нет



Скопировал ВСЕ! smile .
Спасибо участникам за столь подробный анализ.
mes  smile 
Я думаю вопрос решен.
вперед разбираться   с примерами smile 
PM MAIL   Вверх
mes
Дата 26.5.2008, 10:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Podarochek @  26.5.2008,  09:36 Найти цитируемый пост)
Скопировал ВСЕ! 

все не нужно - только последний приведенный код - он самый понятный и "правильный" (насколько можно так сказать)  smile 

Это сообщение отредактировал(а) mes - 26.5.2008, 10:01


--------------------
PM MAIL WWW   Вверх
Podarochek
Дата 26.5.2008, 10:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 94
Регистрация: 2.11.2007

Репутация: нет
Всего: нет



Цитата(mes @  26.5.2008,  10:01 Найти цитируемый пост)
все не нужно - только последний приведенный код - он самый понятный и "правильный" (насколько можно так сказать)  

я думаю, что полезен не только готовый вариант, но и ход размышлений (свои+чужие ошибки)= счастье!
PM MAIL   Вверх
Страницы: (3) [Все] 1 2 3 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1941 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.