LINUX.ORG.RU

Реализация полиморфизма


0

0

Помогите с реализацией полиморфизма; то ли я плохо читал Страуструпа, то ли я много хочу, но ответа на свой вопрос найти не смог. Мне необходимо, что бы функция f() сама определяла наследника и вызывала соответствующий метод. Как это реализовать, подскажите, пожалуйста?

Спасибо.

================================================
#include <iostream>
#include <string>


using namespace std;

// Базовый класс
class Parent
{
public:
void set();
};

class ChildA : public Parent
{
private:
int data;

public:
void set(const int& tdata) { data = tdata; }
};

class ChildB : public Parent
{
private:
string data;

public:
void set(const string& tdata) { data = tdata; }
};


/*
В зависимости от ``data'' f() должна вызывать соответсвующий метод наследника, например, при f(ChildB o, 10) должен вызываться Q.set(int).

*/
template<class T> void f(Parent& Q, T data)
{
Q.set(data);
}

int main(void)
{
ChildA a;
ChildB b;

f(a, 10);
// f(b, "fff");

return 0;
}
================================================

★★★

template<class T,class P> void f(P& Q, T data) { Q.set(data); } int main(void) { ChildA a; ChildB b; int i=10; f(a, i); f(b, "fff"); } Надо же указать функции какой класс мы используем, откуда она может знать о потомках Parenta?

krum
()
Ответ на: комментарий от krum

Указывать явно класс -- это, имхо, самый понятный и легкий способ. Дело в том, что я пишу парсер конфигурационного файла в объектах; там различных типов может быть множество, а интерфейс для чтения -- один.

Поэтому, очень хотелось бы, что тип потомка определялся сам. Спасибо.

kondor ★★★
() автор топика
Ответ на: комментарий от kondor

Так не подойдет?

#include <iostream>

class BaseImpl
{
public:
	virtual ~BaseImpl() {}
};

class D1: public BaseImpl
{
public:
	void func(std::string str) { std::cout << "string: " << str << std::endl; }
};

class D2: public BaseImpl
{
public:
	void func(int i) { std::cout << "int: " << i << std::endl; }
};

class Base
{
private:
	BaseImpl *impl;
public:
	template <class T> Base(T pD): impl(pD) {}
	void func(std::string str)
		{
			D1 *d1 = dynamic_cast<D1*>(impl);
			if(d1)
				d1->func(str);
		}
	void func(int i)
		{
			D2 *d2 = dynamic_cast<D2*>(impl);
			if(d2)
				d2->func(i);
		}
};

int main(void)
{
	D1 d1;
	D2 d2;
	Base B1(&d1);
	Base B2(&d2);
	B1.func("asdf");
	B2.func(2);

	return 0;
}

vasirck
()

2kondor :

То, на чем ты думаешь, к полиморфизму отношения не имеет. Это перегрузка функции.

Полиморфизм получится, если функция f() принимает ссылку на базовый класс и дергает методы этого базового класса. Тогда можно ей подсунуть потомка с должным образом виртуализированными методами.

Die-Hard ★★★★★
()
Ответ на: комментарий от Die-Hard

Вдогонку:

Сразу говорю, в предыдущие советы в топике не вникал, может, повторюсь.

Можно так, например:

Просто вместо f(a,10) и f(b,"fff") надо f(Parent *,void*);

А дочки в виртуальном методе set(void*) приведут параметр к нужному типу.

Die-Hard ★★★★★
()
Ответ на: комментарий от vasirck

vasirick

Спасибо. Идея очень интересная, но она всё равно требует расписывать в классе типы, которые будут переданы в объект; а это опять же подходит.

kondor ★★★
() автор топика
Ответ на: комментарий от Die-Hard

Die-Hard:
Это уже конечно чуть-чуть ближе, но все-равно не то, что я хотел. Если так дело и дальше пойдет (не удасться решить этот вопрос так, как хочу), то придется забить на свой парсер и переходить на XML ;-).

kondor ★★★
() автор топика
Ответ на: комментарий от kondor

я могу показать как это сделано в Gtk+ на С

а с C++ я давненько дела не имел.

cvv ★★★★★
()
Ответ на: комментарий от kondor

Мутно ты объясняешь, что же именно тебе нужно - но (мне кажется), что
нужны тебе фабрики объектов

template<class B, typename P> 
class Factory 
{ 
public: 
  virtual B* create(P p) = 0; // p - это какая-то характеристика типа 
};

что-то типа того? Почитай Александреску "Соврменное проектирование на
С++" (есть в электронном виде).

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

Begemoth

Спасибо, то, что нужно, имхо ;).

Всем спасибо за помощь!

kondor ★★★
() автор топика

// Базовый класс
class Parent
{
public:
    virtual void set(const int& tdata) {}
    virtual void set(const string& tdata) {}
};

И все работает. Или я чего-то не понял?

dissident ★★
()
Ответ на: комментарий от dissident

А одну функцию из этих сетов не сделаешь, шаблоны не умеют быть виртуальными. А кто мешает писать нормально и передавать в set указатель на void* и размер, а следом делать memcpy?

dissident ★★
()
Ответ на: комментарий от dissident

> А кто мешает писать нормально и передавать в set указатель на void* и размер, а следом делать memcpy?

вы извращенец? С каких пор void* - стало нормально? void* нужен в С как костыль в его системе типов.

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

Это просто флейм. Ты предлагал выше фабрику объектов, с фабрикой объектов пришлось делать такие же свитчи внутри как чье-то решение с динамическим приведением. И автор хотел передавать разные параметры методу set*(, с фабрикой этого тоже не сделаешь. Значит внутри созданных фабрикой объектах снова нужны какие-то приведения типов либо совсем другая организация set() (не как у автора поста) не принимающая никаких аргументов. Какая разница? Количество кода такое же самое, но все усложняется, требует 5 минут на понимаение вместо полминуты в C и т.д.

А кстати в Яве с generics'ами виртуальный "шаблон" наверняка вполне бы себе вышел, там ведь нет проблем с невозможностью создать vtable до вызова "шаблона". И автор бы получил результат (может не высокотехнологичный из книжек с 1500 страницами) но работающий сразу.

Я не говорю что Ява хороший язык, просто зачем для парсера брать C++? Для рэй трэйсера ладно, но для парсера?

dissident ★★
()
Ответ на: комментарий от dissident

Может я автора понял не правильно - что ему нужно из конфига разнотипные значения считывать (в общем случае нужна фабрика объектов), ведь он не уточнил какой именно у него конфиг, что за значения. Какой вопрос - такой ответ. Ведь в принципе из конфига можно считывать и значения составных типов (e.g. .fetchmailrc) - хотя в этом случае лучше (по воззможности) парсеру дергать нужный hook, который будет считывать значение и обрабатывать его. И все даже на С будет type safe.

Идеальный парсер - read (или load) в Lisp'е :-)

ЗЫ: на С++ давно не писал - что ты понимаешь под виртуальным шаблоном?

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

> ЗЫ: на С++ давно не писал - что ты понимаешь под виртуальным шаблоном?

class A {
public:
   virtual template<class T> fun(T t) 
   { cout << "base: " << t << endl; }
}

class B : A {
public:
   virtual template<class T> fun(T t) 
   { cout << "child: " << t << endl; }
}

void fun(A &a) { a.fun(...); }

Низзя. А с generics наверняка можно (лень проверять).

dissident ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.