Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Конструктор копирования в абстрактном классе


Автор: BSOD 14.12.2008, 21:07
Есть такая проблема:

Есть абстракстный интерфейс стека (Stack), в нем есть операции Top, Delete, Insert, ForEach и (!) operator= ну, и еще нам нужен оператор копирования
Стек может быть реализован как угодно в классе наследнике.
Возникает проблема реализовать конструктор копирования... В конструкторе копирования я использую виртуальную функцию clone, которую потом перегружаю в каждой реализации, но, в clone-то передается ссылка на Stack (наш интерфейс), чез который не особо-то и можно скопировать...
Вот сейчас думаю, как его реализовать, были такие идеи:
использовать ForEach и Insert... отлично, но мы не можем в ForEach передать функцию-член класса :(
и два варианта:
1. реализовать функцию at(int) для доступа к i-тому элементу стека, но 1. Стек может быть реализован через список, а тогда это плохо 2. Никакого at по условию нет в абстрактном классе
2. написать итератор... но это будет еще хуже впихиваться в условия задачи (пишу не себе ;))... и походу этого делать не буду

Вот. Что собсно можно сделать еще?

Автор: Lazin 14.12.2008, 21:13
Цитата(BSOD @  14.12.2008,  21:07 Найти цитируемый пост)
Вот. Что собсно можно сделать еще?

использовать статический полиморфизм, а не динамический было-бы логичней в данном случае

Автор: BSOD 14.12.2008, 21:26
Нееее, хотя бы потому, что это не подойдет под условие задачи еще больше, чем любой другой изврат...  :(

Автор: Lazin 14.12.2008, 21:40
это не изврат, если ты хочешь реализзовать конструктор копирования, значит тебе нужна семантика значений, тоесть абстрактный базовый класс здесь совершенно не у дел.
конструктор копирования нельзя сделать виртуальным, соответственно в абстрактном базовом классе ему нет места, так как у него по определению нет состояния. 

Автор: BSOD 14.12.2008, 21:43
Почему же, если бы в базом класе были функции для доступа не только к первому элементу, но и ко всем, то все бы реализовывалось, т.е. скорее всего что-то вводить придется, вопрос - что...

Автор: Alek86 14.12.2008, 21:45
можно использовать паттерн Посетитель
типа такого выйдет

есть StackParent и дети: Stack1, Stack2

Код

StackParent& StackParent::operator=(const StackParent& rhs) {
 rhs.CloneTo(*this);
}

void Stack1::CloneTo(Stack1& subj) {
 // реализация копирования из Stack1 в Stack1
}
void Stack1::CloneTo(Stack2& subj) {
 // реализация копирования из Stack2 в Stack1
}

void Stack2::CloneTo(Stack1& subj) {
 // реализация копирования из Stack1 в Stack2
}
void Stack2::CloneTo(Stack1& subj) {
 // реализация копирования из Stack2 в Stack2
}


Добавлено @ 21:46
то же и с copy constructor

хотя не совсем так - в моем случае интерфейс StackParent зависит от наследников
дальше думать  лень, но, вроде, Посетитель и эту проблему решает

Автор: BSOD 14.12.2008, 21:48
Цитата

можно использовать паттерн Посетитель
типа такого выйдет
...


Тогда мы должны знать всех потомков...

Автор: Alek86 14.12.2008, 21:50
точно, надо выделить интерфейс Посетителя, который будет за тех потомков отвечать
подробнее - в инете, по запросу "паттерн Посетитель"

Автор: mes 14.12.2008, 22:06
Цитата(BSOD @  14.12.2008,  21:07 Найти цитируемый пост)
. В конструкторе копирования я использую виртуальную функцию clone, которую потом перегружаю в каждой реализации, но, в clone-то передается ссылка на Stack (наш интерфейс), чез который не особо-то и можно скопировать...


есть интерфейс стека, для копии вызываете виртуальную Clone, за реализацию которой ответственнен конкретный объект, у которого все  нужные методы присутствуют.
Так ? а в чем проблема то ?  smile 
Особенно не понял
Код

в clone-то передается ссылка на Stack (наш интерфейс),

а для чего собственно передается ?? this`a более чем достаточно. 

Автор: Alek86 14.12.2008, 22:09
mes, если есть 2 потомка - stack1 и stack2, то между ними - 4 варианта копирования
если 3 потомка - 6 вариантов
и так далее
а вирт функция - только одна

Автор: Lazin 14.12.2008, 22:12
вот поэтому в таких случаях не используют конструктор копирования. если твой стек поддерживает итераторы, то можно вместо метода clone, добавить методы begin, end и assign smile

Добавлено через 1 минуту и 50 секунд
Код

class StackImpl1;
class StackImpl2;

StackImpl1 s1;
...
StackImpl2 s2(s1.being(), s1.end());

или
Код

StackImpl2 s2;
s2.assign(s1.begin(), s1.end());

Автор: mes 14.12.2008, 22:16
Цитата(Alek86 @  14.12.2008,  22:09 Найти цитируемый пост)
mes, если есть 2 потомка - stack1 и stack2, то между ними - 4 варианта копирования

то есть надо чтоб 
Код

class IStack {...}
class Stack1 : public IStack {..}
class Stack2 : public IStack {..}

..
Stack1 st1;
Stack2 st2;
st1 = st2; // сработало это ?  

тогда Clone, то тут не при чем !

Автор: Alek86 14.12.2008, 22:16
с чего это стеку вдруг поддерживать итераторы?
можно, конечно, с пом pop, top, push скопировать, но это ж будет довольно неэкономично

Добавлено через 23 секунды
mes, да, к примеру

Автор: mes 14.12.2008, 22:23
Цитата(Alek86 @  14.12.2008,  22:16 Найти цитируемый пост)
да, к примеру 

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

Добавлено @ 22:25
Цитата(BSOD @  14.12.2008,  21:07 Найти цитируемый пост)
использовать ForEach и Insert... отлично, но мы не можем в ForEach передать функцию-член класса :(

а функтор можем передавать ? 

Автор: BSOD 14.12.2008, 22:31
Цитата

с чего это стеку вдруг поддерживать итераторы?
можно, конечно, с пом pop, top, push скопировать, но это ж будет довольно неэкономично


На самом деле это всего-лишь в 3 раза "неэкономичнее", чем просто копирование ;)... и этот вариант я тож рассматривал (на крайняк smile )... но.. но была проблема с тем, что копируем мы из константного объекта, т.е. никих pop()... но, т.к. на самом деле - это просто задача по проге, которую нужно сдать всему потоку, я узнал, что народ собирается просто заюзать typeid, если мы копируем из одного потомка в такой же, то статик_каст и копируем, иначе кидаем како-нить эксцепшн... но я так сделать не могу, ибо это совсем уж бред. Но. Вот прям только что я понял, что это можно использовать, что бы создать копию константного объекта, а потом ее уже через pop-push скопировать  smile 
Да, я понимаю, что это совсем жуткий изврат, но всеравно, ведь это будет работать (1), мы не будем менять ничего в абстрактном классе (2) и это даже не будет особо тормозить (!) smile
сорри, что я тут сам с собой нафлудил немного, эмоции smile

Добавлено через 1 минуту и 21 секунду
Цитата

а функтор можем передавать ? 


Эммм, а кстати да, как вариант... но всеравно, придется немного, да подправить базовый класс..

Автор: mes 14.12.2008, 22:38
Цитата(BSOD @  14.12.2008,  22:31 Найти цитируемый пост)
Эммм, а кстати да, как вариант... но всеравно, придется немного, да подправить базовый класс.. 

a foreach что принимает ?? 

Автор: J0ker 14.12.2008, 23:02
Код

struct B
{
    virtual B *Clone() = 0;
};

struct D1: public B
{
    int d1;
    virtual D1 *Clone() {
        return new D1(*this);
    }
};

struct D2: public B
{
    int d2;
    virtual D2 *Clone() {
        return new D2(*this);
    }
};

Автор: BSOD 14.12.2008, 23:08
Код

  virtual B *Clone() = 0;

а в потомке уже
Код

 virtual D1 *Clone()

а так нельзя, получится, что нет реализации чисто виртуальной функции в классе потомке ;) Это как бы было первое, что все пробовали, после чего эта проблема и обнаружилась ;)

Добавлено через 34 секунды
Цитата

a foreach что принимает ??  

Указатель на функцию

Автор: J0ker 14.12.2008, 23:27
Цитата(BSOD @  14.12.2008,  23:08 Найти цитируемый пост)
а так нельзя, получится, что нет реализации чисто виртуальной функции в классе потомке ;) Это как бы было первое, что все пробовали, после чего эта проблема и обнаружилась ;)

можно
перегруженная переопределенная функция может возвращать указатель или ссылку на потомков/предков возвращаемого оригинала

Автор: mes 14.12.2008, 23:33
Цитата(J0ker @  14.12.2008,  23:27 Найти цитируемый пост)
можно
перегруженная функция может возвращать указатель или ссылку на потомков/предков возвращаемого оригинала 


BSOD, мне кажется, имел ввиду что при такой конструкции не получится сделать следующее : 
Код

D1 d1;
D2 d2;
d1=d2;


Добавлено @ 23:35
думаю подойдет такой вот насбросок: 
Код



class IStack
{
   public: 
       void CopyFrom (const IStack&) ; // лучше если не член класса. 
       virtual void ForEach (IOperation& ) const =0 
       virtual void PushBack (int) =0;

class IOperation 
{
    public :
      virtual void operator () (int);
};  
};

class CPushBackInStack: public IStack::IOperation
{
  public:
    CCopyStack (IStack *dst) : p_stack(dst) {}
     virtual void operator () (int i)  { p_stack->PushBack (i); };

   private:
     IStack * p_stack;
};

void IStack::CopyFrom (const IStack& src) 
{
   CPushBackInStack op(this); src.ForEach (op);
}


Автор: J0ker 14.12.2008, 23:44
Цитата(mes @  14.12.2008,  23:33 Найти цитируемый пост)
BSOD, мне кажется, имел ввиду что при такой конструкции не получится сделать следующее : 
Выделить всёкод C++
1:
2:
3:
    
D1 d1;
D2 d2;
d1=d2;

такое и не должно получаться, если D1 и D2 не являются ковариантами
для таких присвоений надо делать либо оператор преобразования типов либо специальный оператор=

Добавлено через 2 минуты и 19 секунд
Цитата(mes @  14.12.2008,  23:33 Найти цитируемый пост)
думаю подойдет такой вот насбросок: 

функцию копирования лучше делать внешней и темплетной - как и сделано в STL
непонятно, зачем изобретать велосипед

Автор: mes 15.12.2008, 00:09
Цитата(J0ker @  14.12.2008,  23:44 Найти цитируемый пост)
такое и не должно получаться, если D1 и D2 не являются ковариантами
для таких присвоений надо делать либо оператор преобразования типов либо специальный оператор=

ну да я сильно упростил, так будет точнее 
Код

d1.CopyFrom (d2);


Цитата(J0ker @  14.12.2008,  23:44 Найти цитируемый пост)
функцию копирования лучше делать внешней и темплетной - как и сделано в STL

согласен...но в классе я оставил ee специально для демонстрации,  мне показалось так для тс будет яснее подходит ли ему такой способ или нет,
а сама функция фактически и не нужна (только как оболочка), всю реализацию несет на себе функтор .

Цитата(J0ker @  14.12.2008,  23:44 Найти цитируемый пост)
непонятно, зачем изобретать велосипед 

как понял для сессии.  smile 

Автор: BSOD 15.12.2008, 00:16
Цитата

можно
перегруженная функция может возвращать указатель или ссылку на потомков/предков возвращаемого оригинала

Я не про то, прост мы же получается не перегрузим функцию из родителя а введем новую. А т.к. в родителе наша функция чисто виртуальная, то компилер будет ругаться, что мы не ее не перегрузили в потомке.

Цитата

думаю подойдет такой вот насбросок: 

Ну вот это я думаю прокатит, но я вроде бы как-то ужн реализовал, сейчас буду смотреть, что получилось smile

Добавлено через 6 минут и 13 секунд
Цитата

ну да я сильно упростил, так будет точнее код C++
1:
  d1.CopyFrom (d2);

ну на самом деле наверное нужно было 
Код

B * p1 = new D1();
B * p2 = new D2();
p1->CopyFrom(D2);

;)

Цитата

как понял для сессии.  

Что-то типа того smile

Автор: mes 15.12.2008, 00:24
Цитата(BSOD @  15.12.2008,  00:16 Найти цитируемый пост)
прост мы же получается не перегрузим функцию из родителя а введем новую. А т.к. в родителе наша функция чисто виртуальная, то компилер будет ругаться, что мы не ее не перегрузили в потомке.

А Вы попробуйте и будете сильно удивлены  smile 
Только такая технология позволяет только клонирование, но не копирование объекта от потомка к  потомку другого типа.  smile 

Автор: J0ker 15.12.2008, 00:25
Цитата(BSOD @  15.12.2008,  00:16 Найти цитируемый пост)
прост мы же получается не перегрузим функцию из родителя а введем новую

нет, мы именно перегрузим переопределим

Цитата(BSOD @  15.12.2008,  00:16 Найти цитируемый пост)
то компилер будет ругаться,

не будет

Автор: mes 15.12.2008, 00:28
...

Автор: J0ker 15.12.2008, 00:30
вы меня запутали млин
не перегрузка конечно, а переопределение

Автор: BSOD 15.12.2008, 20:34
Цитата

А Вы попробуйте и будете сильно удивлены  smile 
Только такая технология позволяет только клонирование, но не копирование объекта от потомка к  потомку другого типа.  smile  


Сорри, немного не досмотрел, так действительно должно работать smile
Сделал почти так, только Клон ничего не возвращает, а в него передается ссылка на другой стек, куда он себя и копирует smile

Всем спасибо, вопрос вроде как исчерпан smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)