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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Доступ к полям потомков, Без приведения типа... 
:(
    Опции темы
CPlusPlusFAN
Дата 20.2.2006, 21:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 1.11.2005
Где: Воронеж

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



Имеется класс:
Код

class cl
{
/*Поля класса cl*/
};


Ещё есть 2 класса:
Код

class cl1:public cl
{
/*Поля класса cl1*/
};

class cl2:public cl
{
/*Поля класса cl2*/
};


И класс-контейнер:
Код

class cont
{
cl *elem;
const char*ClassName;
public:
cont(int):ClassName("cl1"){elem=new cl1;}
cont(int,int):ClassName("cl2"){elem=new cl2;}
cl* operator ->(){return elem;}//Вот здесь проблема...
~cont(){delete elem;}
};


Класс cont содержит элемент базового класса cl и имя действительного класса ClassName.
А вот оператор стрелка возвращает указатель на elem. Но вот проблема, с помощью оператора можно получить доступ только к базовому классу. А можно ли получить доступ (без явного приведения) именно к полю elem как к классу cl1 или cl2 (в зависимости от того, какой конструктор выбран)?
Я понимаю, что elem формируется во время выполнения, но всё же...
PM MAIL ICQ Jabber   Вверх
chipset
Дата 20.2.2006, 22:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 4071
Регистрация: 11.1.2003
Где: Seattle, US

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



Шаблоны, специализация конструктора и dynamic_cast спасут тебя.


--------------------
Цитата(Jimi Hendrix)
Well, I stand up next to a mountain
And I chop it down with the edge of my hand
PM MAIL WWW   Вверх
CPlusPlusFAN
Дата 21.2.2006, 00:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 1.11.2005
Где: Воронеж

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



Спасибо, я тоже думал про шаблоны, а можно пример?
PM MAIL ICQ Jabber   Вверх
chipset
Дата 21.2.2006, 01:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 4071
Регистрация: 11.1.2003
Где: Seattle, US

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



Примерно так.
Код

template <typename T>
cl* operator ->(){
return dynamic_cast<T*>(elem);
}

или так
Код

template <class T>
class cont
    
{
    
cl *elem;
    
const char*ClassName;
    
public:

cont(int,int);
cont(int);



    
cl* operator ->(){return dynamic_cast<cl2>(elem);}//Вот здесь проблема...
    
~cont(){delete elem;}
    
}; //class cont
template<> cont<cl1>::cont(int a, int b) {}
template<> cont<cl2>::cont(int a) {}





--------------------
Цитата(Jimi Hendrix)
Well, I stand up next to a mountain
And I chop it down with the edge of my hand
PM MAIL WWW   Вверх
MAKCim
Дата 21.2.2006, 09:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

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



Цитата

Примерно так.
Код

template <typename T>
cl* operator ->(){
return dynamic_cast<T*>(elem);
}


зачем приводить все к T* а затем возвращать заново cl*?
кроме того ты уверен, что cl - полифорфный, чтобы применять dynamic_cast?

Это сообщение отредактировал(а) MAKCim - 21.2.2006, 09:28


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

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


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 4071
Регистрация: 11.1.2003
Где: Seattle, US

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



Цитата(MAKCim @ 20.2.2006, 23:27 Найти цитируемый пост)
зачем приводить все к T* а затем возвращать заново cl*?

Ступил smile

Цитата(MAKCim @ 20.2.2006, 23:27 Найти цитируемый пост)
кроме того ты уверен, что cl - полифорфный, чтобы применять dynamic_cast?

Нет. А у тебя есть метода получше?smile


--------------------
Цитата(Jimi Hendrix)
Well, I stand up next to a mountain
And I chop it down with the edge of my hand
PM MAIL WWW   Вверх
threef
Дата 21.2.2006, 11:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 375
Регистрация: 27.10.2005
Где: Запорожье

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



Вообще-то это типичный пример полиморфизма, ИМХО применять преобразования - извращение натурально. Виртуальные методы доступа к элементам класса описываются в базовом, перекрываются в производных. Кстати, перегрузка
ссылки - типичный пример, но можно и так:
Код

class _1{
public:
   virtual int get()=0;
};
class _2:public _1{
   int x;
public:
   int get(){ return x; }//перекрываем метод предка
};
class _3:public _1{
   int x,y;
public:
   int get(){ return x+y; }//перекрываем метод предка
};

[quote]
 class cont
{
_1 *elem;
const char*ClassName;
public:
cont(int):ClassName("cl2"){elem=new _2;}
cont(int,int):ClassName("cl3"){elem=new _3;}
cl* operator ->()
{
[/quote]

return elem->get();//где-то так
[quote]

}
~cont(){delete elem;}
};

 

[/quote]


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


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

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



Цитата

Код

class _1{
public:
   [B]virtual ~_1() {}[/B]
   virtual int get()=0;
};
class _2:public _1{
   int x;
public:
   int get(){ return x; }//перекрываем метод предка
};
class _3:public _1{
   int x,y;
public:
   int get(){ return x+y; }//перекрываем метод предка
};
[quote]
 class cont
{
_1 *elem;
const char*ClassName;
public:
cont(int):ClassName("cl2"){elem=new _2;}
cont(int,int):ClassName("cl3"){elem=new _3;}
cl* operator ->()
{
[/quote]
return elem->get();//где-то так
[quote]
}
~cont(){delete elem;}
};




--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

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


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

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



Цитата

Нет. А у тебя есть метода получше?

может это
Код

...
using std::auto_ptr;

template<class Base, class List> class container
{
private:
    auto_ptr<Base> pointer;
public:
    class bad_convert {...};
    container(int a)
        : pointer(new typename index_type<List,0>::type) {}
    container(int a, int b)
        : pointer(new typename index_type<List,1::type>) {}
    ...
    template<class U> U* get()
    {
        U* ptr=convert<Base,U>::cnvt(pointer);
        assert<bad_convert>(ptr);
        return ptr;
    }
};
...
class A
{
public:
    virtual ~A()=0;
};

A::~A() {}

class B: public A {};
class C: public A {};

int main()
{
    container<A,typelist2(B,C)> w(345);
    w.get<B>()...;          //как сделать w.get()... я не знаю
    return 0;
}

List - список типов производных от Base классов длины равной кол-ву перегруженных версий конструктора
Base - базовый класс
index_type - определяет тип в списке типов по его порядковому номеру в нем
convert - определяет является ли U - производным от Base
cnvt - статический метод convert, пытается сконвертировать pointer в U*

про списки типов см. Александреску smile


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

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


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 1.11.2005
Где: Воронеж

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



У меня начинает складываться впечатление, что эта задача вообще не разрешима без приведения типа извне...

За время решения задачи мне на ум пришло 3 возможных метода:
1) общий потомок. Класс, унаследованный от 2-х потомков класса cl. Этот метод не пашет, т.к. поля одного из родительских классов будут неинициализированы.
2) шаблон. Оформление оператора-селектора в виде шаблона, но при этом при доступе к классу требуется явно задать тип в <>.
3) виртуальные методы возврата. Возврат полей класса виртуальной(ыми) функцией(ями). Если возвращать по одному полю из функции, то количество функций будет меняться при добавлении/удалении полей, да и при несовпадении количества и типов полей у разных потомков это приводит в тупик. Если возвращать структуры полей, то для всех потомков будет 1 структура на всех (т.е. общая), но при инициализации часть полей будет неопределена (и пользователь может перепутать поля, особенно, если их много).

Итак, все 3 способа привели в тупик... Может, у кого-то есть ещё предложения???
PM MAIL ICQ Jabber   Вверх
Earnest
Дата 21.2.2006, 21:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Экс. модератор
Сообщений: 5962
Регистрация: 17.6.2005
Где: Рязань

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



Цитата(CPlusPlusFAN @ 21.2.2006, 19:56 Найти цитируемый пост)
Итак, все 3 способа привели в тупик...

Это потому, что ты пытаешься родить общую теорию всего. smile
Задачу поставь поконкретней...

А вообще приведенный тобой класс cont напоминает попытку сваять smart-pointer, т.е. псевдо-указатель, инкапсулирующий управление памятью. Если дело только в этом, то задача давно успешно решена товарищами Мейерсом, Александреску,etc... Да вот хотя бы boost::smart_pointer...



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


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 1.11.2005
Где: Воронеж

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



Цитата
Задачу поставь поконкретней...

Итак уже поставил, конкретней некуда:

необходимо, чтобы указатель elem в классе cont возвращал указатель на корректный класс (cl1 или cl2). Т.е., чтобы пользователю не приходилось делать что-то вроде:

Код

if (a->ClassName=="cl1") (dynamic_cast<cl1*>(elem))->...;
else (dynamic_cast<cl2*>(elem))->...;


Это сообщение отредактировал(а) CPlusPlusFAN - 21.2.2006, 21:59
PM MAIL ICQ Jabber   Вверх
np9mi7
  Дата 22.2.2006, 00:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 553
Регистрация: 17.8.2003
Где: Volgograd, Russia

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



Цитата
У меня начинает складываться впечатление, что эта задача вообще не разрешима без приведения типа извне...


Это смотря как задачу поставить:

1.) Если действительный тип не известен на этапе компиляции, то придеться делать dynamic_cast либо определять общий интерфейс;

2.) Если действительный тип известен на этапе компиляции, то использование шаблонов даст красивое решение, без такого ужаса:

Код
if (a->ClassName=="cl1") (dynamic_cast<cl1*>(elem))->...;
else (dynamic_cast<cl2*>(elem))->...;
, за который руки нужно отрывать (прости CPlusPlusFAN - ничего личного). Если хочеться так делать, то typeid есть, а вообще использования таких вещей нужно как огня бояться;

Это сообщение отредактировал(а) np9mi7 - 22.2.2006, 00:24


--------------------
"Я точно знаю то, что ничего не знаю..." Сократ.
evolution project
PM MAIL WWW ICQ MSN   Вверх
CPlusPlusFAN
Дата 23.2.2006, 01:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 315
Регистрация: 1.11.2005
Где: Воронеж

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



np9mi7, действительный тип неизвестен, ведь пользователь сам волен выбирать, какой из конструкторов выбрать:
Код

cont(int):ClassName("cl1"){elem=new cl1;}
cont(int,int):ClassName("cl2"){elem=new cl2;}

Цитата(np9mi7 @ 22.2.2006, 00:17 Найти цитируемый пост)
за который руки нужно отрывать

А думаете мне это нравится?

Цитата(np9mi7 @ 22.2.2006, 00:17 Найти цитируемый пост)
то придеться делать dynamic_cast либо определять общий интерфейс


А вот отсюда можно поподробней?
PM MAIL ICQ Jabber   Вверх
np9mi7
  Дата 23.2.2006, 13:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 553
Регистрация: 17.8.2003
Где: Volgograd, Russia

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



CPlusPlusFAN, перед тем как всё это прочтешь, поймешь всю сложность задачи и уродливость любого из его решений - задай себе вопрос "Тип точно не известен на этапе компиляции?"

Итак, если тебе тип не известен, то стандартнее делать так:

1) dynamic_cast:

Базовый тип:
Код

class CBase
{

        CBase (const CBase &);
        const CBase& operator = (const CBase &);

    public:
        CBase (){}
        virtual ~CBase (){};
};


Определение потомков:
Код

class CChild1 : public CBase
{
        const char m_cValue;

        CChild1 (const CChild1 &);
        const CChild1& operator = (const CChild1 &);

    public:
        CChild1 () : m_cValue (){}
        virtual ~CChild1 (){};
        
        char GetCharValue () const                                    /// УНИКАЛЬНОЕ свойство объекта
        {
            return m_cValue;
        }
};

class CChild2 : public CBase
{
        const int m_iValue;

        CChild2 (const CChild2 &);
        const CChild2& operator = (const CChild2 &);

    public:
        CChild2 () : m_iValue (){}
        virtual ~CChild2 (){};
        
        int GetIntValue () const                                    /// УНИКАЛЬНОЕ свойство объекта
        {
            return m_iValue;
        }
};


Используй почти стандартный boost::shared_ptr вместо написания своего owner - а
Код

typedef boost::shared_ptr <CBase> CBasePtr;


Функция которая будет работать с конткетным типом, имея только абстрактный тип:
Код

void SomeFunction (CBasePtr ptrBase)
{
    if (typeid ( * ptrBase.get ()) == typeid (CChild1))
    {
        CChild1 * const pChild1 = 
            dynamic_cast <CChild1 * const> (ptrBase.get ());
        
        /// ...
        
        pChild1->GetCharValue ();                               /// использование УНИКАЛЬНОГО свойства объекта
        
        /// ...

        return;
    }
    else if (typeid ( * ptrBase.get ()) == typeid (CChild2))
    {
        CChild2 * const pChild2 = 
            dynamic_cast <CChild2 * const> (ptrBase.get ());
        
        /// ...
        
        pChild2->GetIntValue ();                                /// использование УНИКАЛЬНОГО свойства объекта
        
        /// ...

        return;
    }
    
    throw std::logic_error ("Unknown child type!");
}


2) Общий интерфейс

Базовый тип:
Код

class CBase
{

        CBase (const CBase &);
        const CBase& operator = (const CBase &);

    public:
        
        CBase (){}
        virtual ~CBase (){};

        virtual char GetCharValue () const = 0;                        /// ОБЩИЙ интерфейс
        virtual int GetIntValue () const = 0;                        /// ОБЩИЙ интерфейс
};


Определение потомков:
Код

class CChild1 : public CBase
{
        const char m_cValue;

        CChild1 (const CChild1 &);
        const CChild1& operator = (const CChild1 &);

    public:
        CChild1 () : m_cValue (){}
        virtual ~CChild1 (){};
        
        virtual char GetCharValue () const                            /// реализация ОБЩЕГО интерфейса
        {
            return m_cValue;
        }

        virtual int GetIntValue () const                            /// реализация ОБЩЕГО интерфейса
        {
            throw std::logic_error ("Method not implemented!");
        }
};

class CChild2 : public CBase
{
        const int m_iValue;

        CChild2 (const CChild2 &);
        const CChild2& operator = (const CChild2 &);

    public:
        CChild2 () : m_iValue (){}
        virtual ~CChild2 (){};
        
        virtual char GetCharValue () const                            /// реализация ОБЩЕГО интерфейса
        {
            throw std::logic_error ("Method not implemented!");
        }

        virtual int GetIntValue () const                            /// реализация ОБЩЕГО интерфейса
        {
            return m_iValue;
        }
};


Используй почти стандартный boost::shared_ptr вместо написания своего owner - а
Код

typedef boost::shared_ptr <CBase> CBasePtr;


Функция которая будет работать с конткетным типом, имея только абстрактный тип (необходимо следить за исключениями):
Код

void SomeFunction (CBasePtr ptrBase)
{
    /// ...
    ptrBase->GetCharValue ();                                       /// использование ОБЩЕГО интерфейса объекта
    /// ...

    /// ...
    ptrBase->GetIntValue ();                                           /// использование ОБЩЕГО интерфейса объекта
    /// ...
}


Это сообщение отредактировал(а) np9mi7 - 24.2.2006, 01:11


--------------------
"Я точно знаю то, что ничего не знаю..." Сократ.
evolution project
PM MAIL WWW ICQ MSN   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
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.1123 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


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

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