Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: 1 [2] 3 4 5 6   Вниз
  Печать  
Автор Тема: борьба с шаблонами  (Прочитано 133351 раз)
0 Пользователей и 2 Гостей смотрят эту тему.
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #30 : 31-01-2009 21:34 » 

Вад, реальные типы можно сравнить через
Код: (C++)
typeid(*this) == typeid(T)
(требует include<typeinfo>).
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #31 : 01-02-2009 06:45 » 

сделал родительский класс весь шаблоном - оказалось так будет лучше . Только разнести в h и cpp не удалось - компилиться всё компилилось, а потом всё, что в cpp - unresolved symbol )))
Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #32 : 01-02-2009 07:08 » 

Если я правильно всё помню, по стандарту весь код шаблона должен быть в заголовке. На то есть резон: весь код шаблона должен быть доступен по месту специализации шаблона, поскольку шаблон сам по себе - не тип: он описывает семейство типов (классов или функций). И каждая специализация генерирует свой отдельный тип (входящий в семейство), с полным набором методов, используя код шаблона. Ведь для каждого типа-аргумента шаблона требуется сгенирировать набор методов по заданному образцу (вот хотя бы как в примере с new-delete: нужно для каждого типа сгенерировать отдельный код, который вызывает именно для этого типа операторы new или delete). Примерно такой принцип.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #33 : 01-02-2009 07:23 » 

а ещё такой вопрос - по идее, конструктор и деструктор в данном коде после подстановки в шаблон будут иметь имена Child и ~Child()
Почему это не вызывает конфликта с именами методов в самом классе Child , вызовы происходят нормально- порядок проверил в отладчике (1,2,3,4)

Код:
template<class T>
class Parent
{
public:
Parent<T>()
{
// 1
}

virtual ~Parent<T>()
{
// 4
}
};

class Child : public Parent<Child>
{
public:
Child()
{
// 2
}

virtual ~Child()
{
// 3
}
};

void main()
{
Child A;
}
Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #34 : 01-02-2009 07:38 » 

Parent<Child> - это полноценный тип, его в общем случае можно использовать непосредственно (как vector<int>, например). У него будут свои конструктор и деструктор, сгенерированные из шаблона.
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #35 : 06-02-2009 08:24 » 

какая то непонятная проблема.

сначала пример, который работает как надо
Код:
struct sss0
{
void fff0()
{
int iii=1;
OnReceive();
}

virtual void OnReceive()
{
int iii=1;
}
};


template<class T>
struct sss1: public sss0
{
virtual void OnReceive();
virtual void MyReceive();
};

template<class T>
void sss1<T>::OnReceive()
{
MyReceive();
}

template<class T>
void sss1<T>::MyReceive()
{
int iii=1;
}


struct sss2:public sss1<sss2>
{
virtual void MyReceive();
};

void sss2::MyReceive()
{
int iii=1;
}


вызов:

sss2 S2;
S2.fff0();
//как и хотелось, попадаем в -> sss2::MyReceive()


--------------------------------------------------------------
теперь так:
Код:
template<class T, int nN>
class CParent: public CAsyncSocket
{
private:
virtual void OnReceive(int nErrorCode);

virtual void MY_OnReceive();
};


template<class T, int nN>
void CParent<T,nN>::OnReceive(int nErrorCode)
{
MY_OnReceive();
}

template<class T, int nN>
void CParent<T,nN>::MY_OnReceive()
{
int iii=1;
}

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

class Child : public CParent<Child,10>
{
public:
virtual void MY_OnReceive();
};

void Child::MY_OnReceive()
{
int iii=1;
}

CAsyncSocket - это MFC-шный асинхронный сокет, у него имеется виртуальная void OnReceive() , которая вызывается, когда пришли какие то данные. Так вот, происходит почему то следующее:

Код:
//где то внутри вызвалась
CAsyncSocket::OnReceive();

//попали в
CParent<T,nN>::OnReceive()

//пришли в
CParent<T,nN>::MY_OnReceive()

//хотя должны были придти в Child::MY_OnReceive() !!!


где ошибка ?


Код выше и ниже вроде одинаковый, либо у меня уже просто ступор и что то я не вижу Жаль
« Последнее редактирование: 06-02-2009 08:27 от Алексей1153++ » Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #36 : 06-02-2009 09:20 » new

Алексей1153++, private-члены не наследуются. Вообще сочетание private и virtual смысла не имеет. Замени private на protected.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #37 : 06-02-2009 09:33 » 

не помогло
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #38 : 06-02-2009 09:36 » 

а чего эт они, кстати, не наследуются ? Непонимяу
Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #39 : 06-02-2009 09:38 » 

Алексей1153++, потому что они private. Доступны только данному классу (ну и друзьям, емнип). По стандарту так Улыбаюсь
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #40 : 06-02-2009 09:43 » 

тогда почему же

template<class T>
struct sss1: public sss0
{
   virtual void OnReceive();
   private: virtual void MyReceive();
};

sss2 S2;
S2.fff0();

прекрасно работает ? )
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #41 : 06-02-2009 12:43 » 

Алексей1153++, потому что в обоих классах оба методы (с одинаковой сигнатурой) объявлены виртуальными. В таблице виртуальных функций есть только одна ссылка на метод с такой сигнатурой. Объект с точки зрения класса-предка думает, что обращается к своему закрытому методу на уровне класса предка, но тот же объект с точки зрения класса-потомка, конструктор которого выполнялся позже конструктора предка, заменил для этой сигнатуры старый адрес на адрес своего метода.

Тем не менее наследования тут нет. Это два одноимённых метода, причём второй (в потомке) заменяет первый. Достигаемый эффект полиморфизма - это торчащие из реализации языка уши, то, что называется "дырявой абстракцией". Смена сигнатуры метода у потомка приведёт к развалу всего механизма работы.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #42 : 06-02-2009 14:54 » 

ну переделал на protected, один хрен, загадка какая то
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #43 : 06-02-2009 15:32 » 

уже матерюсь вслух )))

ну никак...

а вот так попробовал - всё отлично
Код:
template<class T, int nN>
class CParent: public CAsyncSocket
{
protected:
void OnConnect(int nErrorCode);
virtual void MY_OnReceive();
};


template<class T, int nN>
void CParent<T,nN>::OnConnect(int nErrorCode)
{
MY_OnReceive();
}

template<class T, int nN>
void CParent<T,nN>::MY_OnReceive()
{
int iii=1;
}

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

class Child : public /*CAsyncSocket//*/CParent<Child,10>
{
protected:
void MY_OnReceive();
};

void Child::MY_OnReceive()
{
int iii=1;
}


Child SSSSS;
SSSSS.Create(0,SOCK_STREAM,FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE);

int sailen;
sockaddr_in sai;
sailen=sizeof(sai);
sai.sin_family=AF_INET;
sai.sin_port=0;
sai.sin_addr.s_addr=0;

SSSSS.Connect((sockaddr*)&sai,sailen);


после неудачного коннекта ожидаемо попадаем в CParent<T,nN>::OnConnect , потом из него в Child::MY_OnReceive()

.

В моём же классе какая то загадко, я уже фигею Улыбаюсь)))
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #44 : 06-02-2009 16:40 » 

вот выяснил путём экспериментов удивительную весчь - это две функции определены в CAsyncSocket абсолютно одинаково

   virtual void OnReceive(int nErrorCode);
   virtual void OnConnect(int nErrorCode);

также в Parent они одинаково переопределены.

И самое странное: если поставить вызов MY_OnReceive() в OnConnect, всё правильно вызывается, а если в OnReceive - то неправильно, вызывается метод CParent<T,nN>::MY_OnReceive()

хм....
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #45 : 06-02-2009 17:44 » 

Лично меня всякое на вид бесцельное "колбашение" кода не вдохновляет. Хочешь помощи - объясни, какую задачу ты решаешь.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #46 : 06-02-2009 17:49 » 

всего лишь виртуальные функции поиметь - в родителе вызовется , скажем, OnAccept, там клиентский сокет подключится, а затем в дитё должно вызваться нечто вроде virtual OnAfterAccept() для дополнительных операций после подключения клиента
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #47 : 06-02-2009 18:58 » 

Код: (C++)
class Connection
 {
  protected:
   virtual void onOpened() = 0;
   virtual void onClosed() = 0;
  public:
   void open()
    {
     this->onOpened();
    }
   void close()
    {
     this->onClosed();
    }
 };

class MyConnection:
  public Connection
 {
  protected:
   virtual void onOpened()
    {}
   virtual void onClosed()
    {}
 };

Причём тут шаблоны и многоуровневые наследники, я не знаю. Если нужны уточнения и второй уровень потомков, то:
Код: (C++)
class MySpecificConnection:
  public MyConnection
 {
  protected:
   virtual void onOpened()
    {
     MyConnection::onOpened();
    }
   virtual void onClosed()
    {
     MyConnection::onClosed();
    }
 };
(На собираемость и работоспособность не проверял.)
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #48 : 06-02-2009 19:28 » 

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

Я всё делаю правильно, загвоздка похоже в шаблоне - сейчас избавляюсь от него снова, оставлю лишь NEW и DELETE . Потом о результатах отпишусь
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #49 : 06-02-2009 21:10 » 

Алексей1153++, я не не понял, я тебе показал, что никаких загвоздок в реализации такой идеи нет.

Цитата: Алексей1153++
чистые виртуальные
Ррр.

Цитата: Алексей1153++
оставлю лишь NEW и DELETE
Я вообще не понимаю, зачем эти методы. Ты в начале сказал "не важно, зачем, просто удобно". Не вижу я в них никакого удобства. Все такие действия выполняют конструктор и деструктор самого объекта, т.е. достаточно использовать обычные операторы new и delete. Если нужно абстрагироваться от конкретного типа, то использовать шаблон проектирования "фабричный метод"/"виртуальный конструктор" и виртуальный деструктор.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #50 : 07-02-2009 06:06 » 

воистину ррр )  Я щас сам побултыхаюсь, может усё получится
Записан

Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #51 : 07-02-2009 22:17 » 

а вот разобрался.

(кстати, заодно понял, что в конструкторе и деструкторе виртуальные функции ведут себя плохо Улыбаюсь )

причина была такая :

в классе-шаблоне
template<class T>
class Child : Parent ...

создавал новый экземпляр так
Child<T>* ... = new Child<T>

поэтому вызывались виртуальные функции класса Child<T> , а не T

сделал так
T* ... = new T
 - теперь всё нормально.

А долбился долго так изза того, что компилятор считает типы Child<T> и T при приведении - одинаковыми! И спокойно делал неявные приведения


(Child<T>*) T*
и
(T*) Child<T>*
« Последнее редактирование: 22-08-2012 05:18 от Алексей1153++ » Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #52 : 07-02-2009 23:01 » 

Без комментариев.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #53 : 08-02-2009 00:35 » 

А я прокомментирую Улыбаюсь Это не шаблоны зло, это беспорядочные связи беспорядочное наследование и эксплуатирование предков - зло. Шаблоны тут вообще ни при чём. Замени шаблон на обычный класс, подставив туда вместо T* свой Child* (или чем там шаблон специфицируешь) - получишь тот же набор развлечений "кто кого пытается вызвать".

По мне, может статься, стоило изначально обернуть этот сокет аккуратненько, чтобы не распускал свои виртуальные методы куда не просят. Для чего вообще требовалось его наследовать?
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #54 : 08-02-2009 07:15 » 

По мне, может статься, стоило изначально обернуть этот сокет аккуратненько, чтобы не распускал свои виртуальные методы куда не просят. Для чего вообще требовалось его наследовать?
так с этого всё и начиналось )  Сначала без всяких шаблонов хотелось обойтись. Но понадобилось в родительском классе поиметь массив из дочерних классов, и тогда началось:

чтобы создать объект дитя, надо неким образом вызвать new Child в родителе, а это возможно только, если применить виртуальную функцию, которая отработает внутри самого дитя (при чём - уже реально созданного объекта, поэтому виртуальная функция и отпала. Осталось применить шаблон, в котором все действия по выделению и освобождению памяти - это new T и detete T

отказаться от шаблона таким образом не вышло )

(с шаблонами ранее дела не имел, поэтому так парюсь)
Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #55 : 08-02-2009 09:01 » 

Алексей1153++, не совсем понял. Хранить массив дочек в родителе - для этого полиморфизм есть (допустим, ты синглетон с потомками затеял, и хочешь для всех в иерархии хранить по экземпляру). Но чтобы вызвать конструктор - всё равно нужно знать тип конструированного объекта. То есть, у тебя есть фабрика, которая знает про всех потомков? Тогда зачем вся пляска с шаблонами? Если нет фабрики, то не пойму, как ты создаёшь объекты для всех потомков. Тут уже как-то обсуждалась тема, почему невозможны виртуальные конструкторы Улыбаюсь
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #56 : 08-02-2009 09:25 » 

не знаю, что такое фабрика, у меня сделано так:

класс сокета, производный от CAsyncSoket описывает объект, который можно назначить сервером или клиентом. В сервере создаётся массив для подключенных пар клиентов. И вот от всего этого безобразия можно производить класс, в котором уже не задумываешься об управлении подключениями, а просто назначаешь тип - сервер или клиент.
Пришлось использовать как виртуальные функции так и шаблон )) Может, и не самое красивое вышло решение, но зато работает как нужно теперь
Записан

Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #57 : 08-02-2009 10:20 » 

А может, кесарю - кесарево, а слесарю - слесарево? То есть, сокеты (включая слушающий у сервера) отдельно, а хранение клиентских подключений - отдельно? (кстати, у клиента тоже возможно несколько подключений?) Какой смысл городить огород в потомке самого сокета?
Записан
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #58 : 08-02-2009 15:43 » 

массив пар в клиенте не создаётся, а в сервере выделяется через new. Ежели всё красиво получится - в виде статьи попробую написать )
Записан

Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #59 : 08-02-2009 18:59 » 

Цитата: Алексей1153++
Ежели всё красиво получится - в виде статьи попробую написать )
А я бы не советовал.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Страниц: 1 [2] 3 4 5 6   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines