Имя пользователя:
Пароль:  
Помощь | Регистрация | Забыли пароль?  | Правила  

Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Программирование и базы данных » C/C++ - Присвоение функции значения

Ответить
Настройки темы
C/C++ - Присвоение функции значения

Ветеран


Сообщения: 1396
Благодарности: 131


Конфигурация

Профиль | Отправить PM | Цитировать


Изменения
Автор: mrcnn
Дата: 06-11-2008
Код: Выделить весь код
class E
{
public:
	virtual BOOL L(int) = 0;								
};
L - функция
Что означает "= 0" и правильно ли это?

И еще вопрос, как определяется
Код: Выделить весь код
E* p;
p->L;
какую из имплементированных функций L вызвать? Где он их ищет?

Отправлено: 10:04, 06-11-2008

 
pva pva вне форума

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить PM | Цитировать


Цитата mrcnn:
virtual BOOL L(int) = 0; »
это так называемая "чисто виртуальная функция", у которой нет описания (такая запись). Используется для описания абстрактных классов, для чего служит - описано в любом учебнике по ООП.
Цитата mrcnn:
E* p;
p->L; »
в данном коде явная ошибка: создан указатель, который смотрит на случайный адрес, потом пытаются взять адрес виртуальной функции, как если бы адресу указателя располагался экземпляр класса E. Причём адрес нигде не используется. Правильней было бы сделать так (если я понял что имеется ввиду):
Код: Выделить весь код
class E_implemetiation : public E
{
public:
   bool L(int n)
   {
      return (std::cout << "L(" << n << ")").good();
   }
}

...

E_implementation E_implementation1;
E* p = &E_implementation1;

...

p->L(1);
Это сообщение посчитали полезным следующие участники:

Отправлено: 12:14, 06-11-2008 | #2



Для отключения данного рекламного блока вам необходимо зарегистрироваться или войти с учетной записью социальной сети.

Если же вы забыли свой пароль на форуме, то воспользуйтесь данной ссылкой для восстановления пароля.


Ветеран


Сообщения: 1396
Благодарности: 131

Профиль | Отправить PM | Цитировать


Цитата:
в данном коде явная ошибка:
Код: Выделить весь код
E* p;
p->L;
Это не реальный код, а теоретический. Все имена - теоретические. Первой строкой я хотел показать, что это указатель на класс E и ничего больше. Надо было добавить многоточия? Предполагалось, что он уже инициализирован нормальным адресом класса при обращении к нему во второй строке.
Затем производится обращение к чистой виртуальной функции. Я не совсем понимаю механизм, по которому будет вызвана функция, стоящая за этим L, но функция, которая будет вызвана, определена, где-то в другом месте. Переопределять L в этом классе вообще нельзя. Надо переопределять то, что стоит за этим L. Но как допустим найти, какая именно? Я не понимаю взаимосвязи между вот этой чисто виртуальной функцией, и той функцией, которая будет вызвана при обращении к ней, т.е. p->L.
Класс E не является наследником других классов, но у него есть наследники.

В реальном рабочем коде примерно следующее.

Код: Выделить весь код
class E
{
public:
	virtual BOOL L(int) = 0;
...
};


class T : public R, public E
{
...
}

class C : public E
{
public:
...
};

class F
{

public:
	virtual BOOL LT(const z&, E*);	
};



BOOL F::LT(const z& p, E* px)
{
if(!px || !px->L(1))  // что за функция L будет вызвана? 
	return FALSE;
}

-------
Ehhh.. what's up, doc?..


Последний раз редактировалось mrcnn, 06-11-2008 в 17:10.


Отправлено: 16:37, 06-11-2008 | #3


Новый участник


Сообщения: 49
Благодарности: 17

Профиль | Отправить PM | Цитировать


Цитата mrcnn:
по которому будет вызвана функция, стоящая за этим L, »
Но ведь L это функция и есть???

p->L это E::L

Отправлено: 03:44, 07-11-2008 | #4

pva pva вне форума

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить PM | Цитировать


Обрисую как это устроено. Любой полиморфный класс (то есть содержащий виртуальные функции) содержит внутреннюю переменную - указатель на таблицу виртуальных функций.
Код: Выделить весь код
class E
{
private: virtual void mem_fun()
  {
     // do something
  }
public: virtual bool L(int)
  {
     // do something
  }
}

// кодируется как (условный код):
struct E_vtbl
{
   int base_offset = 0;
   mem_fun_address = &mem_fun; // адрес кода mem_fun
   L_address = &L; // адрес кода L
}

struct E_class
{
    E_vtbl* _vtbl = &E_vtbl;
}

// таким образом вызов виртуальной функции идёт так:
// p->mem_fun();
(*p->_vtbl->mem_fun_address)(p + p->_vtbl->base_offset);

// p->L(1);
(*p->_vtbl->L_address)(p + p->_vtbl->base_offset, 1);
У чистых виртуальных функций считается что указатель в виртуальной таблице является нулём (оттуда и запись), но на самом деле бывает что такой таблицы вообще нет, а бывает, что там находится код, который выдаёт сообщение об ошибке "pure virtual function called". Таким образом:
Код: Выделить весь код
class E
{
int a;
public: virtual bool L(int) = 0;
}

class E1 : public E
{
int b;
public: virtual bool L(int)
  {
     cout << "E1::L\n";
  }
}

class E2 : public E
{
double c;
public: virtual bool L(int)
  {
     cout << "E2::L\n";
  }
}

// кодируется как:
struct E1_vtbl
{
   base_offset = 0;
   L_address = &E1::L // E_vtbl, заполненная правильным адресом E1::L
}

struct E1_vtbl_E
{
   base_offset = -4;
   L_address = &E1::L // E_vtbl, заполненная правильным адресом E1::L
}

struct E2_vtbl
{
   base_offset = 0;
   L_address = &E2::L // E_vtbl, заполненная правильным адресом E2::L
}

struct E2_vtbl_E
{
   base_offset = -4;
   L_address = &E2::L // E_vtbl, заполненная правильным адресом E2::L
}

struct E1_class
{
// общие для всех
    E1_vtbl* _vtbl = &E1_vtbl; // таблица E1
// составляющий класс E
    E_vtbl* _vtbl1 = &E1_vtbl_E; // таблица E
    int a; // данные E

    int b; // общие данные
}

struct E2_class
{
// общие для всех
    E2_vtbl* _vtbl = &E2_vtbl; // таблица E2
// составляющий класс E
    E_vtbl* _vtbl1 = &E2_vtbl_E; // таблица E
    int a; // данные E

    double c; // общие данные
}
1. преобразование указателя к E - вычисление адреса, где находится составляющий класс: E_ptr = E2_ptr + 4;
2. вызов E::L - из виртуальной таблицы (*E_ptr->_vtbl->L)(E_ptr+E_ptr->_vtbl->base_offset, 1). В результате, если посчитать, вызов преобразуется в E2_ptr->E::L2
Для чего нужен base_offset? Дело в том, что в C++ классы обладают множественным виртуальным наследием. Чтобы программа могла при реализации вызова могла преобразовать адрес к наследнику от любого базового класса (который может и не "знать", в состав чего входит). Иногда base_offset вводят в тело функции. Идёт вызов кода, который вносит изменения в указатель this и делает прыжок на функцию класса-наследника
Это сообщение посчитали полезным следующие участники:

Отправлено: 08:26, 07-11-2008 | #5



Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Программирование и базы данных » C/C++ - Присвоение функции значения

Участник сейчас на форуме Участник сейчас на форуме Участник вне форума Участник вне форума Автор темы Автор темы Шапка темы Сообщение прикреплено

Похожие темы
Название темы Автор Информация о форуме Ответов Последнее сообщение
Разное - WLAN, присвоение адреса, шарик в иконке, в трее бегает, хотя уже подключён _ib_ Microsoft Windows 2000/XP 5 16-09-2009 16:26
Присвоение прав на системные файлы и папки. Удаление скрытого администратора. D@n1k007 Автоматическая установка Windows 10 / 8 / 7 / Vista 10 27-10-2008 04:46
XP. Присвоение IP сетевому интерфейсу Stesh Хочу все знать 2 02-10-2006 18:43
Значения параметров h00ligan Автоматическая установка Windows 2000/XP/2003 1 21-06-2005 12:23
Значения абривеатур Trojn Хочу все знать 11 21-05-2003 23:58




 
Переход