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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Что делать с возвращаемым значением в потомке, Ситуация при перегрузке operator=() 
:(
    Опции темы
EvilsInterrupt
Дата 27.7.2009, 12:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Executables research
***


Профиль
Группа: Завсегдатай
Сообщений: 1019
Регистрация: 14.7.2007
Где: Железнодорожный, МО, Россия

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



Есть Base и Child классы, в них перегружены оператор operator=

т.е код:
Код

class Base
{
public:
  virtual  Base & operator=(const Base & b);
};

class Child : public Base
{
public:
  virtual Base & operator=(const Child& c);
};

Base & Child::operator=(const Child & c)
{
   Base::operator=(c);  // Что делать с возвращаемым значением этого метода ?
   return(*this);
}


Вопрос в коментарии ;)
PM MAIL WWW ICQ Jabber   Вверх
mes
Дата 27.7.2009, 13:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

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



а Вас не смущает, что сигнатура у Ваших операторов разная ?
И вобще какого поведения Вы хотите добиться, делая оператор=  виртуальным ?



--------------------
PM MAIL WWW   Вверх
GoldFinch
Дата 27.7.2009, 13:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата



****


Профиль
Группа: Завсегдатай
Сообщений: 2141
Регистрация: 30.11.2008

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



EvilsInterrupt, значение возвращаемое бинарными операторами нужно чтобы была возможность его использовать
например foo(a=b), c=(a=b)
если ты не собираешься чтото с ним делать - игнорируй его.

PM MAIL ICQ   Вверх
Леопольд
Дата 28.7.2009, 07:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

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



Цитата(EvilsInterrupt @ 27.7.2009,  12:52)
Вопрос в коментарии ;)

Если я правильно понял вопрос, то ответ такой. А вообще, желательно задавать более исчерпывающие вопросы. Так больше шансов получить нужный ответ.
Код

#include <iostream>

class Base{
public:
    virtual Base& operator= (Base const& rarg) = 0;
};

class Child: public Base{
public:
    virtual Base& operator= (Base const& rarg){
        if(&rarg != this){
            std::cout<<"virtual Child::operator="<<std::endl;
        }
        return *this;
    }
    Child& operator= (Child const& rarg){
        if(&rarg != this){
            std::cout<<"Child::operator="<<std::endl;
        }
        return *this;
    }
};

int main(int argc, char* argv[])
{
    Child obj0;
    Child obj1;
    Child obj2;
    obj0 = obj0 = obj1 = obj2;

    Base& ref0 = obj0;
    Base& ref1 = obj1;
    Base& ref2 = obj2;
    ref0 = ref0 = ref1 = ref2;

    Base* ptr0 = &obj0;
    Base* ptr1 = &obj1;
    Base* ptr2 = &obj2;
    *ptr0 = *ptr1 = *ptr2;

    *ptr2 = ref0 = obj1;
}


Это сообщение отредактировал(а) Леопольд - 28.7.2009, 07:38


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
mes
Дата 28.7.2009, 09:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

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



Цитата(Леопольд @  28.7.2009,  06:11 Найти цитируемый пост)
    *ptr2 = ref0 = obj1;

хотел бы посмотреть как автор справится с правильной реализаций поведения этой семантики  smile 

Цитата(Леопольд @  28.7.2009,  06:11 Найти цитируемый пост)
  virtual Base& operator= (Base const& rarg){

тут можно писать 
Код

  virtual Child& operator= (Base const& rarg)

тогда в выражении
Код

child1=child2=base;

в первом присваивании вызовется нужный оператор =(const Child&), а не =(const Base&), как в примере выше.
 smile 

Цитата(Леопольд @  28.7.2009,  06:11 Найти цитируемый пост)
            std::cout<<"virtual Child::operator=()"<<std::endl;

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

например тип параметра :
Код

       std::cout<<"virtual Child::operator=(const Base&)"<<std::endl;
       ..
       std::cout<<"virtual Child::operator=(const Child&)"<<std::endl;




Это сообщение отредактировал(а) mes - 28.7.2009, 09:24


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


Executables research
***


Профиль
Группа: Завсегдатай
Сообщений: 1019
Регистрация: 14.7.2007
Где: Железнодорожный, МО, Россия

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



Вопрос возник в связи следующей ситуаций
Есть базовый - Base1
и есть потомки Child1, Child2, ... ,ChildN

Начал реализовывать копирующее присваивание и копирующую инициализацию. Чтобы имея уже созданного и рабочего потомка Child1 можно было запросто присвоить в только что созданного потомка Child2 .

Пример:
Код

Child1 * pChild1 = new Child1();
Child2 * pChild2;

// Много кода
pChild2 = pChild1; // [1] копирующее присванивание

while(work)
{
Child3 * pChild3 ( pChild2 ); // [2] копирующая инициализация
}


 Вывод : в базовом нужно указать что в потомка будут оператор присваивания и копирующий конструктор, при этом они не будут запрещены(т.е. не закинуты в приват часть), тогда программер делающий реализацию очередного потомка будет точно знать , что ему нужно реализовать! Т.е. Base для него будет - чертежом.

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

Возникает вопрос если в базовом методе присваивания возвращает тип ссылку на себя:
 Base & operator=(const Base & rBase);
то при вызове этого метода в потомке что делать с этим возвращаемым значением ?

+ Вы правильно заметили про сигнатуру, т.е. прототип метода присваивания в потомке, особо не понимаю каким он должен быть(о прототипе) ? Точь в точь как и в базовом или нет?

ЗЫ:
И прошу прощения за мои сумбурные темы ;) К сожалению не адвансед тех. врайтер, но стараюсь хоть чуточку описать по понятней, но как обычно "хотел как лучше, а вышло как всегда" ;)

Это сообщение отредактировал(а) EvilsInterrupt - 28.7.2009, 10:42
PM MAIL WWW ICQ Jabber   Вверх
bsa
Дата 28.7.2009, 10:30 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

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



EvilsInterrupt, обычно, классы, которые имеют виртуальные методы, не присваивают друг другу. Так как присваивание потомка предку или потомку с параллельной ветки по любому приведет к "срезке" (т.е. скопируются данные только общего базового класса). Поэтому с подобными объектами работа ведется через указатели или ссылки.
Как вариант, можно сделать виртуальный метод clone, который будет возращать указатель на клон текущего объекта. А чтобы организовать семантику присваивания, то необходимо все это завернуть в "умный указатель". Поведение которого такое же как у класса, но сам он содержит только указатель на базовый класс (что-то вроде паттерна pimpl).
Пример базового класса:
Код
class Base
{
public:
    //Base(); //какие-то конструкторы - определяешь сам, какие
    virtual Base* clone() const = 0; //создает клон текущего объекта класса и возвращает на него указатель
    virtual ~Base(){} //деструктор должен быть виртуальным, так как есть виртуальные методы
protected:
    Base(const Base &) {} //конструктор копирования (его нужно реализовать, так как будет использоваться в потомках)
private:
    Base& operator=(const Base &); //реализовывать не надо - запрет на прямое присваивание объектов класса
   //тут атрибуты и приватные методы
};

class Derived : public Base
{
public:
   virtual Derived* clone() const { return new Derived(*this); }
protected:
   Derived(const Derived& obj) : Base(obj) { /* тут, наверное, какие-то еще действия */ }
private:
   //тут атрибуты и приватные методы
};


Это сообщение отредактировал(а) bsa - 28.7.2009, 10:41
PM   Вверх
EvilsInterrupt
Дата 28.7.2009, 10:44 (ссылка)    | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Executables research
***


Профиль
Группа: Завсегдатай
Сообщений: 1019
Регистрация: 14.7.2007
Где: Железнодорожный, МО, Россия

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



Цитата

Base& operator=(const Base &); //реализовывать не надо - запрет на прямое присваивание объектов класса

Почему ?
Мне бы приятно было видеть с эстетической точки зрения код вида:

mem_strm = file_strm;

чем:
mem_strm = file_strm.clone();

да и наглядней ;)
PM MAIL WWW ICQ Jabber   Вверх
Леопольд
Дата 28.7.2009, 10:45 (ссылка)  | (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

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



Цитата(mes @ 28.7.2009,  09:24)
тогда в выражении
Код

virtual Child& operator= (Base const& rarg)

в первом присваивании вызовется нужный оператор =(const Child&), а не =(const Base&), как в примере выше.
 smile

Мне кажется что правильнее всё же будет вызывать через один интерфейс. А если возвращать Child то он всё равно будет сведён к ссылке на базовый класс (интерфейс)

Видимо требуется вот такое поведение, только я пока не понял зачем. Если эти классы инкапсулируют различные данные, то каким образом осуществлять присвоение по ссылке на базовый? А если данные одинаковые, то нафига нужны разные классы? smile Использовать dynamic_cast накладно, и говорит о недопроектировании. А поддерживать и расширять будет... Можно подумать как реализовать что-то похожее через шаблоны. Но это, по моему, не так просто smile
Код

#include <iostream>

class Base{
public:
    virtual Base& operator= (Base const& rarg) = 0;
};

class Child0: public Base{
public:
    virtual Base& operator= (Base const& rarg){
        if(&rarg != this){
            std::cout<<"virtual Child0::operator="<<std::endl;
        }
        return *this;
    }
    Child0& operator= (Child0 const& rarg){
        if(&rarg != this){
            std::cout<<"Child0::operator="<<std::endl;
        }
        return *this;
    }
};

class Child1: public Base{
public:
    virtual Base& operator= (Base const& rarg){
        if(&rarg != this){
            std::cout<<"virtual Child1::operator="<<std::endl;
        }
        return *this;
    }
    Child1& operator= (Child1 const& rarg){
        if(&rarg != this){
            std::cout<<"Child1::operator="<<std::endl;
        }
        return *this;
    }
};


int main(int argc, char* argv[])
{
    Child0 obj0;
    Child1 obj1;

    obj0 = obj1;
    obj1 = obj0;
}





--------------------
вопросов больше чем ответов
PM MAIL   Вверх
mes
Дата 28.7.2009, 10:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

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



Цитата(EvilsInterrupt @  28.7.2009,  09:44 Найти цитируемый пост)
Почему ?
Мне бы приятно было видеть с эстетической точки зрения код вида:


Цитата(bsa @  28.7.2009,  09:30 Найти цитируемый пост)
а. А чтобы организовать семантику присваивания, то необходимо все это завернуть в "умный указатель". Поведение которого такое же как у класса, но сам он содержит только указатель на базовый класс (что-то вроде паттерна pimpl).


Добавлено через 4 минуты и 53 секунды
http://forum.vingrad.ru/index.php?showtopi...t&p=1869791
еще один пример  (правда немного устаревший), но ниже в теме есть комменты типа исправления smile


--------------------
PM MAIL WWW   Вверх
bsa
Дата 28.7.2009, 11:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Модератор
Сообщений: 9185
Регистрация: 6.4.2006
Где: Москва, Россия

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



Цитата(EvilsInterrupt @ 28.7.2009,  10:44)
Цитата

Base& operator=(const Base &); //реализовывать не надо - запрет на прямое присваивание объектов класса

Почему ?
Мне бы приятно было видеть с эстетической точки зрения код

Потому что ты напрямую не будешь использовать указанные классы:
Код
class Container
{
public:
//...
    Container(const Container &c) : ptr_(c.ptr_.get() ? c.ptr_->clone() : 0) {}
    Container& operator=(const Container &c) {
        ptr_.reset( c.ptr_.get() ? c.ptr_->clone() : 0);
    }
//тут делаешь методы, которые перекидывают вызовы методам Base.
private:
    std::auto_ptr<Base> ptr_;
};
А вот объекты Container ты можешь уже легко присваивать друг другу.
PM   Вверх
mes
Дата 28.7.2009, 11:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

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



Цитата(Леопольд @  28.7.2009,  09:45 Найти цитируемый пост)
Мне кажется что правильнее всё же будет вызывать через один интерфейс. А если возвращать Child то он всё равно будет сведён к ссылке на базовый класс (интерфейс)

тогда зачем второй оператор = добавили в наследника ?! 
В Вашем случае получается что, в в первом вызове оператора (для a1=a2)
при a1=a2=a3  вызовется =(const ChildA&)
при a1=а2=b1 вызовется  =(const Base&)

имхо, не очень логично.. как в принципе и само решние делать данный оператор виртуальным.



--------------------
PM MAIL WWW   Вверх
Леопольд
Дата 28.7.2009, 11:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

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



Цитата(EvilsInterrupt @ 28.7.2009,  10:24)
Вопрос возник в связи следующей ситуаций
Есть базовый - Base1
и есть потомки Child1, Child2, ... ,ChildN
Пример:
Код

Child1 * pChild1 = new Child1();
Child2 * pChild2;

// Много кода
pChild2 = pChild1; // [1] копирующее присванивание

while(work)
{
Child3 * pChild3 ( pChild2 ); // [2] копирующая инициализация
}

Для присвоения указателей они дожны быть одного типа. Может имелось ввиду следующее?
// Много кода
*pChild2 = *pChild1; // [1] копирующее присванивание

Можно написать конструктор принимающий ссылку на базовый класс, который будет инициализирвать общую, базовую часть
Код

#include <iostream>

class Base{
    int data;
public:
    Base(int data_): data(data_){}
};

class Child0: public Base{
public:
    Child0(void): Base(0) {}
    Child0(Base& general_data): Base(general_data){
        std::cout<<"Child0(Base& general_data)"<<std::endl;
        //остальные действия
    }
};

class Child1: public Base{
public:
    Child1(Base& general_data): Base(general_data){
        //остальные действия
        std::cout<<"Child1(Base& general_data)"<<std::endl;
    }
};


int main(int argc, char* argv[])
{
    Child0 obj0;
    Child1 obj1(obj0);
    Child0 obj2 = obj1;
}


Добавлено @ 11:15
Цитата(mes @ 28.7.2009,  11:08)
имхо, не очень логично.. как в принципе и само решние делать данный оператор виртуальным.

согласен, больше не кажется smile сам не пойму целесообразность виртуального operator= Хотя, если в базовом классе "сидят" какие-то перманентно общие данные...

Это сообщение отредактировал(а) Леопольд - 28.7.2009, 11:18


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
mes
Дата 28.7.2009, 11:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

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



Цитата(bsa @  28.7.2009,  10:05 Найти цитируемый пост)
        ptr_.reset( c.ptr_.get() ? c.ptr_->clone() : 0);

добавлю, что вместо 0, можно использовать объект dummy класса, тогда
1. не нужна будет проверка в методах враппера ("умного указателя") на нуль .
2. ответы на get методы будут реализованы в наследнике (этом dummy классе), а не во враппере.

Добавлено через 1 минуту и 32 секунды
Цитата(Леопольд @  28.7.2009,  10:11 Найти цитируемый пост)
Можно написать конструктор принимающий ссылку на базовый класс, который будет инициализирвать общую, базовую часть

можно.. но в общем случае этот повлечет потерю части информации, что не очень соответсвует семантики копирования smile




--------------------
PM MAIL WWW   Вверх
Леопольд
Дата 28.7.2009, 11:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 943
Регистрация: 17.6.2009

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



Цитата(mes @ 28.7.2009,  11:15)
но в общем случае этот повлечет потерю части информации, что не очень соответсвует семантики копирования smile

для этого, в этом конструкторе, должны ещё как-то инициализироваться остальные данные (базовая часть инициализируется из объекта другого типа)

Добавлено через 4 минуты и 10 секунд
Цитата(mes @ 28.7.2009,  11:15)
что не очень соответсвует семантики копирования smile

что в свою очередь приводит к потере интуитивности.


--------------------
вопросов больше чем ответов
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.1524 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


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

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