Форум программистов, компьютерный форум CyberForum.ru
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 41, средняя оценка - 4.68
stawerfar
141 / 55 / 4
Регистрация: 14.12.2010
Сообщений: 347
Записей в блоге: 1
#1

Перегрузка конструктора копирования и оператора присвоения - C++

11.02.2012, 14:08. Просмотров 5634. Ответов 18
Метки нет (Все метки)

Всем привет, сразу к делу. После прочтения 11 главы Лафоре столкнулся с такой бедой как понятие перегрузка оператора присвоения. Дело в том что Лафоре говорит что именно оператор присвоения является уникальным и не наследуется. Но когда создаёшь указатель на базовый класс там естественно делаешь все методы виртуальными (для полиморфизма), при раз адресации указателя происходит вызов перегруженного оператора присвоения базового класса а не производного который по факту там находиться!! Как же так? Естественно что я перегрузил эти конструкторы и в производных классах. Код ниже подскажите как мне быть?? Как вызвать нужный мне перегруженный оператор?
Базовый класс
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//класс издательство
class publication
{
    protected: 
        static const int sciMAX=100;//максимальный размер символьного массива
        metring* ch_name;//название
        float f_price;//цена
    public:
        //конструктор без параметров
        publication(void);
        //конструктор с двумя параметрами
        publication(char* ch_name, float f_price);
        //конструктор копирования
        publication(publication&);
        //диструктор
        virtual~publication(void);
        //ввод данных
        virtual void put_into_the_class(void)=0;//чисто виртуальная фукция
        //вывод данных на экран консоли
        virtual void get_out_of_the_class(void);
        //оператор присвоения
        virtual publication& operator = (publication&);
};
Конструктор копирования базового класса
C++
1
2
3
    ch_name = val.ch_name;//копирую указатель
    ch_name->icount++;//увеличиваю счётчик указывающий на количество указателей на строку
    f_price = val.f_price;//копирую другое поле класса
Перегрузка оператора присваения
C++
1
2
3
4
5
6
7
8
9
10
    if(this == &val)//если объект присваивается сам себе то выход
    {return *this;}
    if(ch_name->icount==1)
    {delete ch_name;}//если больше ни кто ни указывает на эту строку то удаляем её
    else
    {ch_name->icount++;}//иначе именьшаем счётчик указателей на строку
    ch_name= val.ch_name;//копирую указатель
    f_price = val.f_price;//копирую другое поле класса
    ch_name->icount++;//увеличиваю счётчик указывающий на количество указателей на строку
    return *this;//возвращаю ссылку на объект
Производный класс
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//класс книга
class book : public publication
{
    private:
        int i_how_meny_pic;//количество страниц
    public:
        //конструктор без параметров
        book(void);
        //конструктор копирования
        book(book&);
        //конструктор с тремя параметрами
        book(int,char*,float);
        //ввод данных
        void put_into_the_class(void);
        //вывод данных на экран консоли
        void get_out_of_the_class(void);
        //операция присвоения
        book& operator = (book&);
};
Конструктор копирования для производного (от класса издательство) класса книга
C++
1
2
3
4
5
6
7
8
//конструктор копирования
book::book(book& val)
{
    ch_name = val.ch_name;//копирую указатель
    ch_name->icount++;//увеличиваю счётчик указывающий на количество указателей на строку
    f_price = val.f_price;//копирую другое поле класса  
    i_how_meny_pic = val.i_how_meny_pic;//копирую количество страниц в книге
}
Перегрузка операции присвоения для производного (от класа издательство) класса книга
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//операция присвоения
book& book:: operator = (book& val)
{
    if(this == &val)//если объект присваивается сам себе то выход
    {return *this;}
    if(ch_name->icount==1)
    {delete ch_name;}//если больше ни кто ни указывает на эту строку то удаляем её
    else
    {ch_name->icount++;}//иначе именьшаем счётчик указателей на строку
    ch_name= val.ch_name;//копирую указатель
    f_price = val.f_price;//копирую другое поле класса
    i_how_meny_pic = val.i_how_meny_pic;//копирую количество страниц в книге
    ch_name->icount++;//увеличиваю счётчик указывающий на количество указателей на строку
    return *this;//возвращаю ссылку на объект
}
Производный класс "тип" от класса издательство
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//класс тип
class type : public publication
{
    private:
        float f_time;//время записи книги в менутах
    public:
        //конструктор без параметров
        type(void);
        //конструктор копирования
        type(type&);
        //конструктор с тремя параметрами
        type(float,char*,float);
        //ввод данных
        void put_into_the_class(void);
        //вывод данных на экран консоли
        void get_out_of_the_class(void);
        //оператор присваения
        type& operator = (type&);
 
};
Конструктор копирования производного(от класса издательство) класса "тип"
C++
1
2
3
4
5
6
7
8
//конструктор копирования
type::type(type& val)
{
    ch_name = val.ch_name;//копирую указатель
    ch_name->icount++;//увеличиваю счётчик указывающий на количество указателей на строку
    f_price = val.f_price;//копирую другое поле класса  
    f_time = val.f_time;//коприрую время записи книги в минутах
}
Перегрузка оператора присваения производного (от класса издательство) класса "тип"
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//оператор присваения
type& type:: operator = (type& val)
{
    if(this == &val)//если объект присваивается сам себе то выход
    {return *this;}
    if(ch_name->icount==1)
    {delete ch_name;}//если больше ни кто ни указывает на эту строку то удаляем её
    else
    {ch_name->icount++;}//иначе именьшаем счётчик указателей на строку
    ch_name= val.ch_name;//копирую указатель
    f_price = val.f_price;//копирую другое поле класса
    f_time = val.f_time;//коприрую время записи книги в минутах
    ch_name->icount++;//увеличиваю счётчик указывающий на количество указателей на строку
    return *this;//возвращаю ссылку на объект
}
Использование инструметрария:
Идей заключается в следующем создается в динамической памяти массив указателей на базовый класс.
И процессе работы программы на уровне пользователя решается какой класс будет создан вот кусок кода
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
            std::cout<<"Выбирите тип книги audy или paper (a,p) :";
            std::cin>>str_val;
            if(str_val=="paper"||str_val == "p")
            {
                *(publist+n)=new book;//выделение памяти под необходимый производный класс
                (*(publist+n))->put_into_the_class();//взятие данный от пользователя
                n++;//увеличение счётчика элиментов массива
                break;//выход из внутреннего цикла
            }
            else if(str_val =="audy"||str_val == "a")//если ползователь ввел аудио книгу
            {
                *(publist+n)=new type;//выделение памяти под необходимый производный класс
                (*(publist+n))->put_into_the_class();//взятие данный от пользователя
                n++;//увеличение счётчика элиментов массива
                break;//выход из внутреннего цикла
            }
Далее предусмотрено динамическое выделение памяти при необходимости т.е. если пользователь не угомонился и продолжает вводить данные в массив вот код:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
if(n>=ilenpublist)//елси не хватает места для элементов в массиве то перевыделяем память
         {
            publication** temp = new publication*[ilenpublist];//временный массив для переброски данных
            for(int i =0 ; i<ilenpublist;i++)
            {   //проверяю тип указателя и выделяю в новый временный массив соответствующую память
                if(dynamic_cast<book*>(*(publist+i)))
                {
                    *(temp+i) = new book;//выделяю память
                    *(*(temp+i))=*(*(publist+i));//побитовое копирование между объектами
                }
                //проверяю тип указателя и выделяю в новый временный массив соответствующую память
                else if(dynamic_cast<type*>(*(publist+i)))
                {
                    
                    *(temp+i) = new type;//выделяю память
                    *(*(temp+i))=*(*(publist+i));//побитовое копирование между объектами
                }
            }
            //освобождаю память для перевыдиления
            for(int i =0 ; i<ilenpublist;i++)
            {delete publist[i];}
            //выделяю новый массив на один элемент больше чем предыдуший
            publist = new publication*[n+1];//указатель на базовый класс
            //возвращаю на место уже введеный данные пользователем
            for(int i=0;i<ilenpublist;i++)
            {   //проверяю тип указателя и выделяю в новый временный массив соответствующую память
                if(dynamic_cast<book*>(*(temp+i)))
                {
                    *(publist+i) = new book;//выделяю память
                    *(*(publist+i))=*(*(temp+i));//копирование между объектами
                }
                //проверяю тип указателя и выделяю в новый временный массив соответствующую память
                else if(dynamic_cast<type*>(*(temp+i)))
                {
                    
                    *(publist+i) = new type;//выделяю память
                    *(*(publist+i))=*(*(temp+i));//копирование между объектами
                }
                
            }
            ilenpublist = n;//контроль реального количества элементов в массиве
            //освобождаю память из под временной переменной
            for(int i =0 ; i<ilenpublist-n;i++)
            {delete temp[i];}
         }
Вот в этой строке 31 и 38 почему то вызывается оператор присвоения базового класса а не соответствующего производного
C++
1
*(*(publist+i))=*(*(temp+i));//копирование между объектами
Как мне разрешить эту ситуацию?

Добавлено через 1 час 11 минут
Если кому интересно, то я решил эту проблему вот так:
для класса "тип"
C++
1
2
3
//преобразую указатель на базовый класс на указатель на соответствующий производныый
//поэлиментное копирование между объектами
*(dynamic_cast<type*>(*(publist+i)))=*(dynamic_cast<type*>(*(temp+i)));
для класса книга
C++
1
2
3
//преобразую указатель на базовый класс на указатель на соответствующий производныый
//поэлиментное копирование между объектами
*(dynamic_cast<book*>(*(publist+i)))=*(dynamic_cast<book*>(*(temp+i)));
Теперь всё работает как надо т.е. вызывается перегруженный оператор присваения именно того класса на который указывает указатель в данный момент.
НО возник вопрос-почему так? Неужели если работаешь с указателями при полиморфизме необходимо постоянно вручную преобразовывать указатели? И почему казалось бы альтернативный способ не приносит тех же результатов
C++
1
2
*(publist+i) = dynamic_cast<book*>(*(publist+i))
*(*(publist+i)) = *(*(temp+i));
Это всё очень странно и не понятно, да и самое удивительно то что перед операцией присвоения я проверял с помощью функции typeid какому классу принадлежит созданный объект. Так вот всё нормально
функция показывала что в памяти содержиться тот или иной но нужный в конкретной ситуации объект, но всеравно продолжал вызываться перегруженный метод присвоения базового класса.
Может кто нибудь объяснить? Ни у Лафоре ни у Павловской я этого не нашёл
0
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
11.02.2012, 14:08
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Перегрузка конструктора копирования и оператора присвоения (C++):

перегрузка оператора присвоения - C++
// radius.cpp: определяет точку входа для консольного приложения. // #include &quot;stdafx.h&quot; #include &lt;iostream&gt; #include &lt;conio.h&gt; ...

Конструктор копирования и перегрузка оператора присваивания - C++
Здравствуйте! Возникла следующая проблема: не могу перегрузить оператор присваивания и сделать конструктор копирования Имеется...

Посмотрите описание конструктора копирования и оператора присваивания с ними все в порядке? После функции add теряются данные - C++
class Person{ char *name; int age; char *floor; char *phone; public: Person(){ cout&lt;&lt;&quot; ok&quot;&lt;&lt;endl; age=0; ...

В чём отличия конструктора копирования и конструктора перемещения? Где и как их нужно использовать? - C++
Помогите разобраться в копирующем и перемещающем конструкторах. В чём их существенное отличие и какой плюс от использования перемещающего...

Перегрузка присвоения - C++
Вообщем, есть некий класс А, который хранит в себе массив: class A { private: int Arr; }; Необходимо создать...

Почему не вызывается ни конструктор копий, ни функция перегруженного оператора присвоения - C++
Привет. Начал изучать C++, дошел до темы перегрузки операторов. И запарился на одном месте. Есть класс Dog: class Dog { public:...

Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
igorrr37
1647 / 1275 / 133
Регистрация: 21.12.2010
Сообщений: 1,932
Записей в блоге: 7
11.02.2012, 15:25 #2
сигнатуры должны совпадать с базовым классом
virtual publication& operator = (const publication&);
type& type::operator = (const publication&);
book& book::operator = (const publication&)
0
stawerfar
141 / 55 / 4
Регистрация: 14.12.2010
Сообщений: 347
Записей в блоге: 1
11.02.2012, 15:58  [ТС] #3
Сейчас попробую...

Добавлено через 6 минут
Нет такой Вариант не годиться т.к. publication я вляется базовым и не знает и уж темболее не имеет соответсвующих полей производных классов. Если сделать как говорите вы то будет потеря данных.
0
retmas
Жарю без масла
859 / 741 / 164
Регистрация: 13.01.2012
Сообщений: 1,694
11.02.2012, 16:06 #4
Цитата Сообщение от stawerfar Посмотреть сообщение
Лафоре говорит что именно оператор присвоения является уникальным и не наследуется
раз вас Лафоре не убелил, то хотя бы подобные конструкции должны вас убедить
Цитата Сообщение от stawerfar Посмотреть сообщение
*(dynamic_cast<type*>(*(publist+i)))=*(dynamic_cast<type*>(*(temp+i)));
еще раз: оператор присвоения является уникальным и не наследуется. и в записи
Цитата Сообщение от stawerfar Посмотреть сообщение
virtual publication& operator = (publication&);
писать virtual бесполезно. не будет он виртуальным.
1
stawerfar
141 / 55 / 4
Регистрация: 14.12.2010
Сообщений: 347
Записей в блоге: 1
11.02.2012, 16:17  [ТС] #5
Спасибо за коментарий, но мне показалось что Вы не поняли сути произходящего.
Да Лафоре сказал что не наследуется, а в моём случае наоборот все время вызывался метот базового класса(то есть можно сказать что "наследовался", по факту это конечно не так это я для объяеснения), когда я вытаскивал содержимое из указателей то там каждый раз были разные производные объекты. Следовательно по словам Лафоре метод "=" базового класса не должен был вызываться. Надеюсь Вы прости те меня за столь подробное объяснение, но надеюсь это прольёт свет на ситуацию.
0
retmas
Жарю без масла
859 / 741 / 164
Регистрация: 13.01.2012
Сообщений: 1,694
11.02.2012, 16:28 #6
вызывается оператор = для статического типа указателя, а не динамического
C++
1
2
3
4
5
6
7
8
publication* p1, p2;
book b1, b2;
 
p1 = &b1;
p2 = &b2;
// вызывается publication::operator= , а не book::operator= ,
// т.к. статический тип объекта, на который указывают указатели, publication
*p1 = *p2;
Добавлено через 1 минуту
вот вам вдогонку. не запуская программы, подумайте, что она выведет... а потом проверьте
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
 
class A
{
    int x;
};
 
class B: public A
{
    int x;
};
 
int
main()
{
    B b;
    A* pb = &b;
    std::cout << sizeof b << "=" << sizeof *pb << std::endl;
    return 0;
}
1
stawerfar
141 / 55 / 4
Регистрация: 14.12.2010
Сообщений: 347
Записей в блоге: 1
11.02.2012, 16:43  [ТС] #7
Вы меня совсем что ли за идиота держите? Либо просто выпендриваетесь передо мной?
Люди обычно спорят когда не понимают либо говорят о разных вещах, так Вот в место того что бы пробовать объяснять то что я и так знаю. Я Вам посоветаю внимательно прочитать мой вопрос, а не придумывать его.

Добавлено через 5 минут
Мой Вам втречны вопрос: объект какого класса находиться в *(*(temp+i))
C++
1
2
*(temp+i) = new book;//выделяю память
*(*(temp+i))=*(*(publist+i));//побитовое копирование между объектами
А уж потом объясняйте мне ...
0
retmas
Жарю без масла
859 / 741 / 164
Регистрация: 13.01.2012
Сообщений: 1,694
11.02.2012, 16:56 #8
Цитата Сообщение от stawerfar Посмотреть сообщение
Я Вам посоветаю внимательно прочитать мой вопрос
а я Вам посоветую излагать его по-русски.
хотели знать почему
Цитата Сообщение от stawerfar Посмотреть сообщение
почему то вызывается оператор присвоения базового класса а не соответствующего производного
C++
1
*(*(publist+i))=*(*(temp+i));//копирование между объектами
так я вам и обьяснил, что
Цитата Сообщение от retmas Посмотреть сообщение
вызывается publication::operator= , а не book::operator= , т.к. статический тип объекта, на который указывают указатели, publication

Не по теме:

Цитата Сообщение от stawerfar Посмотреть сообщение
Вы меня совсем что ли за идиота держите?
до этого вопроса - нет.

2
stawerfar
141 / 55 / 4
Регистрация: 14.12.2010
Сообщений: 347
Записей в блоге: 1
11.02.2012, 17:06  [ТС] #9
Я так и думал!! Я полностью с Вами согласен. И это моё упущение что я не предусмотрел что ни кто ни будет в вчитываться в код. И надо было по лучше формировать вопрос. Дело в том что я заметил на этапе отладки что даже вот тут
C++
1
2
*(temp+i) = new book;//выделяю память
*(*(temp+i))=*(*(publist+i));//побитовое копирование между объектами
не смотря на то что там находиться объект класса "книга" причем и слева и справа(второе не важно), почему-то вызывается "не наследуемая" операция присвоения базового класса.
0
retmas
Жарю без масла
859 / 741 / 164
Регистрация: 13.01.2012
Сообщений: 1,694
11.02.2012, 17:21 #10
ну так в чем проблема? так и должно быть: оператор= не наследуется, поэтому для указателей на publication вызывается publication::operator= . и нет тут никакого "наследования". вы просто запутались, что значит "наследует".
а вот если бы вызывался book::operator= для указателей на publication(при их разыменовании), то это и было бы наследованием

Добавлено через 7 минут
если вам понятно это
Цитата Сообщение от retmas Посмотреть сообщение
C++
1
2
3
4
5
6
7
8
publication* p1, p2;
book b1, b2;
 
p1 = &b1;
p2 = &b2;
// вызывается publication::operator= , а не book::operator= ,
// т.к. статический тип объекта, на который указывают указатели, publication
*p1 = *p2;
то почему не пнятно, что здесь ситуация аналогичная
Цитата Сообщение от stawerfar Посмотреть сообщение
C++
1
2
*(temp+i) = new book;//выделяю память
*(*(temp+i))=*(*(publist+i));//побитовое копирование между объектами
1
stawerfar
141 / 55 / 4
Регистрация: 14.12.2010
Сообщений: 347
Записей в блоге: 1
11.02.2012, 17:27  [ТС] #11
Запутался? Ну тогда объясните что по чему это помогает в этой ситуации
Т.е. если сделать так то все работает т.е.
C++
1
*(dynamic_cast<type*>(*(temp+i)))=*(dynamic_cast<type*>(*(ipublist+i)));
а так нет
C++
1
*(*(temp+i))=*(*(ipublist+i));
Вопрос что находиться в *(*(temp+i))?
следующая функция показывает что это объект класса type "тип"
C++
1
std::cout<<typeid(*(*(temp+i))).name()<<std::endl;//type
У этого объекта есть перегруженные операции присвоения и копирования.
Вопрос тогда почему объект типа "тип" вызвает метод базового класса "издательство".
0
retmas
Жарю без масла
859 / 741 / 164
Регистрация: 13.01.2012
Сообщений: 1,694
11.02.2012, 17:40 #12
не нужно путать статические и динамические типы. первые определяются на этапе компиляции, вторые - на этапе выполнения.
на этапе выполнения в *(*(temp+i)) находится объект типа book(или что там у вас). но компилятору нас*ать, что там на этапе исполнения. на этапе компиляции все что он видит - указатель на publication и генерирует соответствующий код для объектов типа publication. для того, чтобы он на этапе компиляции генерил присваивание book::operator=, придется явно ему это указать, например, так
C++
1
*((book*)*(temp+i))=*((book*)*(ipublist+i));
но это совсем не ок и опасно. для этого и придумали dynamic_cast, который безопасен в плане полиморфного приведения типов на этапе выполнения(!!!), т.е. на этапе выполнения у вас и будет по функциональности это
C++
1
*((book*)*(temp+i))=*((book*)*(ipublist+i));
1
stawerfar
141 / 55 / 4
Регистрация: 14.12.2010
Сообщений: 347
Записей в блоге: 1
11.02.2012, 17:48  [ТС] #13
retmas .. так бы сразу и сказали. Большое спасибо что пролили свет на этот процесс, ну я не удовольствуюсь только Вашим ответом. Позволю себе спросить у Вас где Вы это читали? Я бы хотел поподробней эту ситуацию изучить. Я конечно понимаю что Вы можете мне сказать спросит у Googlе, но всё же может посоветуете где мне поподробней почитать по позднее связывание.
Да и ещё вопрос, на сколько я успел заметить такие проблемы возникают только с оператором присвоения?!
Да и ещё Вы показали довольно странный синтаксис
C++
1
2
1
 *((book*)*(temp+i))=*((book*)*(ipublist+i));
я такого раньше на всречал! Объясните пожалуйста
0
retmas
Жарю без масла
859 / 741 / 164
Регистрация: 13.01.2012
Сообщений: 1,694
11.02.2012, 17:56 #14
Цитата Сообщение от stawerfar Посмотреть сообщение
Вас где Вы это читали?
У Страуструпа детально изложены все моменты языка. Страницы не скажу, т.к. под рукой нет. Но скажу одно - не нашли объяснения какого-то ключевого момента языка - читайте еще раз и внимательнее.

Цитата Сообщение от stawerfar Посмотреть сообщение
довольно странный синтаксис
приведение в стиле С
1
silent_1991
Эксперт С++
4964 / 3040 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
11.02.2012, 17:58 #15
stawerfar, чтобы сразу получить ответ, вы должны были выложить сюда минимальный код, повторяющий ошибку вашего изначального кода. Я, лично, зайдя в эту тему и увидев количество кода, просто сразу ушёл отсюда, потому что разбираться во всём этом - сломать глаза и мозг. Я думаю, так поступили многие. Только retmas был в настроении ответить, за что ему плюс.
2
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
11.02.2012, 17:58
Привет! Вот еще темы с ответами:

Касательно конструктора копирования - C++
Вопрос следующий. У меня есть класс &quot;Точки&quot; есть класс &quot;группы&quot; и есть класс &quot;возможные группы&quot;. 3 класса. в классе &quot;группы&quot; есть...

Не понятна работа конструктора копирования - C++
Добрый день! Читаю Шилдта базовый курс. Дошел до конструктора копии.В книжных примерах вроде все понятно было. Перешел на перегрузку...

Грамотное использование конструктора копирования - C++
Всем добрый вечер. Есть класс list(двухсвязный). Задача гласит: Сгенерировать некоторую последовательность чисел, вывести на экран, и затем...

Неявный вызов конструктора копирования - C++
Здравствуйте, как можно неявно вызвать конструктор копирования 3 способами? Я только 1 найти смог. #include &lt;iostream&gt; class...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Yandex
Объявления
11.02.2012, 17:58
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru