Запуск функции,на которую указатель указывает

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Lev
Сообщения: 45
Зарегистрирован: 14 фев 2005, 11:17

/*
В зависимости от ситуации указатель может указывать на одну функцию или другую (Fun1 или Fun2).
И, когда придет время, необходимо выполнять функцию, на которую указывает указатель

*/

Код: Выделить всё

class bclass{
public:
	// указатель(не обязательно как тут типа) для указания на функцию Fun1 или Fun2
	void *AddressOfFun; 
	virtual int Fun1(int u)
		{return (u*2+1);}
	virtual int Fun2(int u)
		{return (u*2+2);}
	virtual void Choice(int selector)
	{
		if(selector==0) 
		{
			// указатель будет указывать на Fun1
			///AddressOfFun = Fun1;
		}
		else
		{
			// указатель будет указывать на Fun2
			///AddressOfFun = Fun2;
		}
	}
};
void main()
{
	bclass ob;
	ob.Choice(1);

	// нужно выполнить функцию, на которую
	// указывает указатель AddressOfFun
	///ВЫПОЛНИТЬ(ФУНКЦИЮ,на которую указывает ob.AddressOfFun; С ПАРАМЕТРОМ u=2);
}
/*можно, конечно, каждой функции присвоить свой уникальный номер, но когда их станет много?
появятся накладные расходы, т.к. нужно будет switch ставить*/
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

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

Указатель на функцию - член класса может указывать только на метод своего класса. Он не может указывать на метод производного класса

Это происходит потому что в С++ есть множественное и виртуальное наследование. Указатель на метод класса с двумя или несколькими предками (некоторые из которых возможно виртуальные предки) занимает больше чем 4 байта. Он содержит не только адрес первого байта функции, но и некоторую служебную информацию. Поэтому указатель на функцию - член нельзя привести к четырехбайтовому void*

Вариант рашения 1 - Интерфейсы + множественное наследование.
Java - образный подход. Не пользуемся указателями на методы вообше, используем интерфейсы и множественное наследование.
Если надо реализовать callback-вызовы, включаем интерфейс с этими вызовами в список наследования, реализуем его методы, а потом передаем всем объектам, которые хотят нас о чем то информировать указатель this.

Вариант рашения 2 - Интерфейсы + вложенные классы.
Тоже Java - образный подход. В Java это все делается намного прямее, поскольку вложенные классы в Java имеют внутреннюю ссылку на охватывающий объект, в контексте которого они были порождены.
Если надо реализовать callback-вызовы, делаем вложенный класс унаследованный от интерфейса, реализуем в нем методы, а потом передаем всем объектам, которые хотят нас о чем то информировать указатель этот объект-переходник. Этот переходник должен содержать ссылку на охватывающий класс. Кроме того, он должен быть объявлен как friend чтобы иметь доступ к private&protected полям охватывающего класса.

Вариант рашения 3 - хаки
Delphi-образный подход. Мы имеем объекты - события, кторым можно присвоить любой метод любого объекта, и дергаем за них. Поскольку в С++ event'ов нет, нужны хаки. Есть два распространенных хака - boost::function и Loki::Functor.
Принцип у них один - впердоливание указателей на методы в пристойный интерфейс при помощи шаблонов. Поскольку указатели на методы в С++ могут быть размером байтов в двадцать, эти хаки не рискуют инкапсулировать в себе указатель, а размещают его в динамической памяти.
boost::function Имеет в себе много хака. но он работает даже на VC++6.0

Loki::Functor Почти не имеет в себе хака. Внутренний объект-пререходник размещает в специальном спуле для мелких объектов (Зачот). Но требует компилятора, совместимого со cтандартом ansi-iso-iec с++. Микрософтовские компиляторы (кроме самого последнего от 2004 года) отпадают. Так что эта радость доступна только пользователям Intel C++, Metrowerk CodeWarrior C++ или g++.
2B OR NOT(2B) = FF
Lev
Сообщения: 45
Зарегистрирован: 14 фев 2005, 11:17

ok, спасибо.
Для окончательного грузона я пойду читать книгу Андрея Александреску
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Это шутка? Чтобы понимать А. Александреску, надо быть гуру в С++ и при этом разбираться в языках подобных Lisp.
2B OR NOT(2B) = FF
Lev
Сообщения: 45
Зарегистрирован: 14 фев 2005, 11:17

всё, разобрался. Значит я уже не новичёк в програмировании.

Код: Выделить всё

using namespace std;
class bclass;
// определение типа для указания на член класса -на функцию
// функция возвращает int и принимает в качестве параметра int
typedef int (bclass::* TpMemFun)(int);

class bclass{
public:
	// создание указателя на функцию
	TpMemFun pToFun; 
	virtual int Fun1(int u)
		{
			cout << "Fun1 ... \n";
			return (u*2+1);
		}
	virtual int Fun2(int u)
		{
			cout << "Fun2 ... \n";
			return (u*2+2);
		}
	virtual void Choice(int selector)
	{
		if(selector==0) 
		{

			pToFun = Fun1;
		}
		else
		{
			pToFun = Fun2;
		}
	}
};

void main()
{
	bclass ob;
	ob.Choice(1);
	(ob.*(ob.pToFun))(7);
	ob.Choice(0);
	(ob.*(ob.pToFun))(7);
}
Lev
Сообщения: 45
Зарегистрирован: 14 фев 2005, 11:17

Неудобно, то что форум не отображает раскраску текста как в компиляторе.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

А зачем ты объявил Fun1 и Fun2 как virtual?
Если ты перегрузишь их в производном классе, все равно будут работать функции из базового. В С++ нет типа "указатель на виртуальный метод"
2B OR NOT(2B) = FF
Lev
Сообщения: 45
Зарегистрирован: 14 фев 2005, 11:17

Результат выполнения программы:
bclass::Fun2 ...
derived::Fun2 ...
Press any key to continue

Код: Выделить всё

using namespace std;
class bclass;
typedef int (bclass::* TpMemFun)(int);

class bclass{
public:
	TpMemFun pToFun; 
	virtual int Fun1(int u)
		{
			cout << "bclass::Fun1 ... \n";
			return (u*2+1);
		}
	virtual int Fun2(int u)
		{
			cout << "bclass::Fun2 ... \n";
			return (u*2+2);
		}
	virtual void Choice(int selector)
	{
		if(selector==0) 
		{

			pToFun = Fun1;
		}
		else
		{
			pToFun = Fun2;
		}
	}
};
class derived: public bclass{
	virtual int Fun2(int u)
		{
			cout << "derived::Fun2 ... \n";
			return (u*2+2);
		}

};
void main()
{
	/*bclass ob;
	ob.Choice(1);
	(ob.*(ob.pToFun))(7);
	ob.Choice(0);
	(ob.*(ob.pToFun))(7);*/

	bclass obBase;
	derived obDerived;
	bclass *pBase;
	
	pBase = &obBase;
	pBase->Choice(1);
	(pBase->*(pBase->pToFun))(7);

	pBase = &obDerived;
	pBase->Choice(1);
	(pBase->*(pBase->pToFun))(7);
}
Eugie
Сообщения: 708
Зарегистрирован: 17 фев 2004, 23:59
Откуда: SPb

В С++ нет типа "указатель на виртуальный метод"
Обычный указатель на метод работает без проблем в случае виртуальных методов.

В сторону (шепотом): так ли уж нужны указатели на методы? :) ИМХО, в отличие от указателей на функции, без них вполне можно жить.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

В сторону (шепотом): так ли уж нужны указатели на методы?
Согласен, жить можно. В Java даже живут (Если закрыть глаза на java.lang.reflect.Method).
НО лично мне кажется хлопотным применять каждый раз наследование и интерфейсы для получения callback - сущности.
Указатели на методы С++ совершенно неудобоваримы. Их толком никто не использует. Их можно использовать только вместе с шаблонами в телегах типа std::mem_fun или std::mem_fun_ref. Или тот же boost::function.
Между тем, авторам компиляторов было бы проще сделать нормальные closures, которым можно привязать любой метод любого объекта с подходящей сигнатурой. И код вызова через такой closure был бы таким:
mov ecx, this
call method
2B OR NOT(2B) = FF
Ответить