Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.78/9: Рейтинг темы: голосов - 9, средняя оценка - 4.78
0 / 0 / 0
Регистрация: 06.06.2017
Сообщений: 61

Конструктор и оператор перемещения

24.08.2022, 12:23. Показов 2300. Ответов 33
Метки нет (Все метки)

Здравствуйте! Изучил тему конструктор и оператор перемещения, и написал следующий код:
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <stdlib.h>
using namespace std;
class Transfer {
private:
    double* d;
    int count;
    void Free() {
        if (count != 0)
            delete[] d;
        count = 0;
    }
public:
    ~Transfer() {
        Free();
    }
    Transfer(double* new_d, int c)
    {
        Free();
        d = new double[c];
        for (int i = 0; i < c; i++)
            d[i] = new_d[i];
        count = c;
    }
    Transfer() : Transfer(nullptr, 0) {}
    Transfer(Transfer&& t) noexcept {
        this->count = t.count;
        this->d = t.d;
        t.count = 0;
        cout << "Конец работы конструктора переноса\n";
    }
    Transfer& operator=(Transfer&& t) noexcept{
        Free();
        if (this != &t)
        {
            this->count = t.count;
            this->d = t.d;
            t.count = 0;
        }
        cout << "Конец работы оператора переноса\n";
        return *this;
    }
    void Print() {
        cout << "{";
        for (int i = 0; i < count - 1; i++)
            cout << d[i] << ", ";
        if (count >= 1)
            cout << d[count - 1];
        cout << "}\n";
    };
}; 
Transfer GetD(int k) {
    double* d = new double[k];
    for (double* q = d; q != d + k; q++)
        *q = rand();
    Transfer t(d, k);
    cout << "Конец создания массива\n";
    return t;
};
int main()
{
    setlocale(LC_ALL, "Rus");
    srand(time(0));
    double* d = new double[] {12, 8, 5};
    Transfer t(d, 3);
    t.Print();
    t = GetD(100);
 
}
Не могу понять, почему у меня вызывается и конструктор переноса и оператор.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
24.08.2022, 12:23
Ответы с готовыми решениями:

Конструктор перемещения и оператор присвоения для двусвязного списка
Добрый день. Помогите разобраться в вопросом: пишу реализацию двусвязного списка, хотел бы разобраться с конструктором перемещения и...

Конструктор перемещения
Правильно написан конструктор, значения в right нужно обнулять или не нужно? class Test { private: int count_; // Кол-во...

Конструктор перемещения
Здравствуйте, пытаюсь уже некоторое время разобраться с move-семантикой. Честно говоря возникли сложности сразу же. Помогите пожалуйста...

33
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13207 / 6841 / 1823
Регистрация: 18.10.2014
Сообщений: 17,304
25.08.2022, 18:00
Цитата Сообщение от SergMT Посмотреть сообщение
В остальном (вопрос в первую очередь к TheCalligrapher) всё-таки я не понял: это нормально, что вызываются и конструктор и оператор? Или должно быть как-то ещё?
Я был не прав, когда у помянул своем сообщении т.наз. copy elision. На самом деле тут речь идет об обычном return value optimization (RVO), которая не являтся гарантированной даже в С++17. То есть вызов и конструктора, и оператора здесь - совершенно нормально. "Лишний" вызов конструктора может быть, а может и не быть.

Но еще раз: я не знаю компиляторов, которые бы не умели делать RVO в этом коде. Удалось воспроизвести только в MSVC++ и только в отладочном коде.

Цитата Сообщение от SergMT Посмотреть сообщение
Кстати, за этим шквалом критики, так и не прозвучало, что именно нужно поправить, если всё-таки код не валидный (вопрос ко всем)?
Так как а после того, как вы поправли то, на что вам явно указали, отсались еще вопросы?
0
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,825
25.08.2022, 18:03
TheCalligrapher, думаю именно в ней и именно в отладке этот код ТС и запускал.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
25.08.2022, 18:42
Цитата Сообщение от DrOffset Посмотреть сообщение
оратор выше прав
Отказываясь от диспетчеризации по count, операция обнуления последнего смысла уже не имеет. Нуль-указатель сам себе флаг. Код который согласится снизить эффективность для проверки вида if(!obj.d) не хуже того, что будет проверять count. Однако, разыменование обоих (указателя on нуль массива и нуль-пойнтера) это UB. Но проверка указателя легче понимается потребителем библиотеки. Я бы отказался от конструктора по умолчанию, присваивающего nullptr. Только явный конструктор c передачей счетчика и исключением для передачи нуля/нулей (или хотя бы дефолт но создающий вменяемый массив из одного элемента).
0
25.08.2022, 19:45

Не по теме:

Цитата Сообщение от IGPIGP Посмотреть сообщение
или хотя бы дефолт но создающий вменяемый массив из одного элемента
Это оверхэд, зачем дергать аллокатор лишний раз?
Ситуаций, где пустой массив - это нормально(и даже нужно), очень много.

0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
25.08.2022, 20:45
Цитата Сообщение от zayats80888 Посмотреть сообщение
Это оверхэд, зачем дергать аллокатор лишний раз?
Ситуаций, где пустой массив - это нормально(и даже нужно), очень много.
Почему оффтопом, zayats80888? Пустой массив имеет две особенности. Первая - фундаментальная. Математики не могут осилить цельные наборы собственных абстракций. Парадокс "нуля и ничто". Это яркий пример. В программировании есть источник (стрим) чёрного звука. Это источник который стримит ноль. То есть математику всё едино - "В дверь ни кто не заходил" или "Заходили нулевые чуваки и чувихи". Но в предметных областях, такая разновидность шизы разбивается вдребезги. Для нашего случая встают два вопроса (озвучено выше) - нельзя разыменовать указатель полученный от нуль-массива (array to pointer conversion). Следовательно, нужно либо проверять на ноль - количество элементов (а также на ноль сам указатель) и терять скорость (вот и оверхед) или ждать когда бабахнет. А второе - это то, что:
Цитата Сообщение от zayats80888 Посмотреть сообщение
дергать аллокатор
придётся всё едино. Я писал об этом также. zayats80888, выделяя массив из нуля элементов вы выделяете память в свободном хранилище. Потом, соответственно вы должны удалить указатель (не люблю эту терминологию). Указатель нужно удалить иначе утечка. Пикантность в том, что его нельзя разыменовывать так же как и указатель хранящий адрес равный значению nullptr. То есть, можно, но гарантируется UB. Я бы не лез в это болото)
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
25.08.2022, 21:29
IGPIGP, я не понял, причем тут математика и массив(в значении тип). Мы говорим о "динамическом массиве". Если бы, например, std::vector в дефолтном конструкторе аллоцировал память, им бы никто(кроме студентов) не пользовался. Что касается
Цитата Сообщение от IGPIGP Посмотреть сообщение
придётся всё едино
, то нет (или же вопрос в колличестве таких аллокаций). Дефолтная аллокация в один(или не важно сколько) элементов никому не нужна, придется либо реаллоцировать, либо просто деаллоцировать(в случае перемещения, например) в большинстве случаев.

Добавлено через 5 минут
Цитата Сообщение от IGPIGP Посмотреть сообщение
выделяя массив из нуля элементов
И зачем же его выделять?
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
25.08.2022, 22:29
Цитата Сообщение от zayats80888 Посмотреть сообщение
то нет (или же вопрос в колличестве таких аллокаций).
то да. Память аллоцируется. Системных вызовов (в нормальных ОС) ровно одна штука и при выделении нулевого массива и при выделении единичного. Но я же и не настаивал. Можно довольствоваться и нуль-указателем. А вот выделять нуль-массив - путешествие в никуда. zayats80888, я пыхтел текст в четверть страницы не за тем чтобы получить аргумент в виде
Цитата Сообщение от zayats80888 Посмотреть сообщение
то нет (или же вопрос в колличестве таких аллокаций).
Что касается философских рассуждений то они мне кажется важнее всего. Они о том, что ничто (отсутствие), это здоровье (логическое), по сравнению с нулевым присутствием. А уж совмещение обоих - это совсем плохо. В std::vector внутренний массив не создаётся как нулевой. То есть, он или есть или его нет (nullptr). Тут с философской точки зрения всё в порядке.

Добавлено через 1 минуту
Цитата Сообщение от zayats80888 Посмотреть сообщение
И зачем же его выделять?
И я об этом. Однако, в данной теме это и может произойти (см. код).
0
 Аватар для zayats80888
6352 / 3523 / 1428
Регистрация: 07.02.2019
Сообщений: 8,995
25.08.2022, 22:54
Цитата Сообщение от IGPIGP Посмотреть сообщение
я пыхтел текст в четверть страницы не за тем чтобы
Ну вы тогда бы сначала переспросили, что я имел ввиду под
Цитата Сообщение от zayats80888 Посмотреть сообщение
пустой массив
А то сами выдумали про массив нулевого размера и что-то объясняете. Пустой массив - это логическое состояние. И я апеллировал к тому, что для этого состояния, как изначального, веделять память нераразумно, как вы предложили. Вот это:
Цитата Сообщение от zayats80888 Посмотреть сообщение
зачем дергать аллокатор лишний раз?
должно было навести вас на правильное понимание, о чем я веду речь.
0
Комп_Оратор)
Эксперт по математике/физике
 Аватар для IGPIGP
9007 / 4708 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
26.08.2022, 07:35
Цитата Сообщение от zayats80888 Посмотреть сообщение
А то сами выдумали про массив нулевого размера и что-то объясняете.
Вы же не читаете ни тему ни то что я пишу. Я повторю в последний раз:
Цитата Сообщение от IGPIGP Посмотреть сообщение
И я об этом. Однако, в данной теме это и может произойти (см. код).
Если в конструктор:
C++
1
2
3
4
5
6
7
8
 Transfer(double* new_d, int c)
    {
        Free();
        d = new double[c];
        for (int i = 0; i < c; i++)
            d[i] = new_d[i];
        count = c;
    }
вторым параметром, передать 0 выделение нулевого массива неизбежно. zayats80888, я написал не много и там всё уже есть. Даже с разбавлениями в виде лирических отступлений о смысле жизни. Не вижу смысла добавить что-либо еще. Написать о Лоренц сокращении длины и возрастании массы? О том как в воображении мат-дегенератов (нуль-воображении) физическое тело становится, геометрически нулевым, приобретая бесконечную плотность? Чёрные дыры отдыхают, хотя и они тоже вымысел, для залепливания дыр в несшиваемой модели.
0
0 / 0 / 0
Регистрация: 06.06.2017
Сообщений: 61
26.08.2022, 10:33  [ТС]
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Удалось воспроизвести только в MSVC++ и только в отладочном коде.
То есть, для других компиляторов код не годится? Вы же вроде написали
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Но еще раз: я не знаю компиляторов, которые бы не умели делать RVO в этом коде
Вообще после того, как я поправил согласно сообщению DrOffset я не заметил никакой разницы.
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <iostream>
#include <cstdlib> // !
using namespace std;
 
class Transfer {
private:
    double* d;
    int count;
    void Free() {
        if (count != 0) {
            delete[] d;
            count = 0;
        }
    }
public:
    ~Transfer() {
        Free();
    }
    Transfer(const double* new_d, int c) // !
        : d(new double[c]), count(c)
    {
        for (int i = 0; i < c; i++)
            d[i] = new_d[i];
    }
    Transfer() 
        : Transfer(nullptr, 0) 
    { }
    
    Transfer(Transfer&& t) noexcept // !
        : d(t.d), count(t.count)
    {
        t.count = 0;
        cout << "Конец работы конструктора переноса\n";
    }
    Transfer& operator=(Transfer&& t) noexcept 
    {
        // !
        if (this != &t) {
            swap(t.d, d);
            swap(t.count, count);
        }
        cout << "Конец работы оператора переноса\n";
        return *this;
    }
 
    void Print() {
        cout << "{";
        for (int i = 0; i < count - 1; i++)
            cout << d[i] << ", ";
        if (count >= 1)
            cout << d[count - 1];
        cout << "}\n";
    }
}; 
Transfer GetD(int k) {
    double* d = new double[k];
    for (double* q = d; q != d + k; q++)
        *q = rand();
    Transfer t(d, k);
    delete[] d; // !
    cout << "Конец создания массива\n";
    return t;
};
int main()
{
    setlocale(LC_ALL, "Rus");
    srand(time(0));
    int c = 3;
    double* d = new double[c] {12, 8, 5};
    Transfer t(d, c);
    delete[] d; // !
    t.Print();
    t = GetD(100);
}
Может глобально что-то неправильно, но в этом локальном примере, где я не вызываю массивы нулевой длины, вроде бы мой код работает точно так же как и его.
Кстати что Вы понимаете под
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
"Лишний" вызов конструктора может быть, а может и не быть.
В зависимости от компилятора? Или от чего?
0
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,825
26.08.2022, 14:54
Цитата Сообщение от SergMT Посмотреть сообщение
но в этом локальном примере, где я не вызываю массивы нулевой длины, вроде бы мой код работает точно так же как и его.
В этом заключается некоторая философия: вроде бы код работает точно так же, но один правильный (с учетом дополнения в #20), а другой нет.
И успешная компиляция не означает отстутвие ошибок в коде. По большому счету вы и ваше знание стандарта определяют будете ли вы собирать код с ошибками или нет.

Цитата Сообщение от SergMT Посмотреть сообщение
то есть, для других компиляторов код не годится? Вы же вроде написали
Нет, если речь про "лишний" вызов конструктора. Постараюсь на пальцах.
1) То, что ваш код вызывает конструктор перемещения - это формально правильно. Это несколько странно в отношении современных компиляторов, но правильно. Иными словами так и должно быть.
Вызов конструктора у вас осуществляется при возврате из функции GetD.
2) Вызов оператора перемещения у вас осуществляется из функции main при присваивании t = GetD(100);.
3) На других компиляторах, или других настройках вашего текущего компилятора, у вас не будет вызова конструктора, потому что он оптимизируется. Поведение программы с наличием этой оптимизации и с ее отсутствием предполагается одинаковое. Т.е. внешне ничего не изменится.
1
0 / 0 / 0
Регистрация: 06.06.2017
Сообщений: 61
30.08.2022, 19:17  [ТС]
Dr Offset, я правильно понял, что в VS19, на которой я работаю мой код и предложенный отработают одинаково, а другие компиляторы могут увидеть ошибку?
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13207 / 6841 / 1823
Регистрация: 18.10.2014
Сообщений: 17,304
30.08.2022, 19:31
Цитата Сообщение от SergMT Посмотреть сообщение
То есть, для других компиляторов код не годится?
Что значит "не годится"???

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

Где вы в своем коде видите какие-то проблемы от этого лишнего копирования? Какая вам разница, скопируют ли ваш объект 10, 5 или 0 раз, если копия эквивалентна оригиналу? Почему "не годится"?

Цитата Сообщение от SergMT Посмотреть сообщение
В зависимости от компилятора? Или от чего?
В С++ абсолютно все - в зависимости от компилятора.

То есть да, в зависимости от компилятора, в т.ч. от настроек этого компилятора.
0
19501 / 10106 / 2461
Регистрация: 30.01.2014
Сообщений: 17,825
30.08.2022, 19:33
Цитата Сообщение от SergMT Посмотреть сообщение
я правильно понял, что в VS19, на которой я работаю мой код и предложенный отработают одинаково, а другие компиляторы могут увидеть ошибку?
Нет, неправильно.
Ваш код имел вполне определенные ошибки связанные с утечкой памяти, которые были исправлены в "предложенном" коде. Так что ни о какой одинаковой работе речи не идет.

Что касается заглавия темы касательно:
Цитата Сообщение от SergMT Посмотреть сообщение
у меня вызывается и конструктор переноса и оператор.
то это вообще ошибкой не является нигде.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
30.08.2022, 19:33

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

C++11 Конструктор перемещения
Добрый день. Решил тут познакомится с конструктором перемещения, и сразу протестировал кое-что. Конструктор перемещения просто изымает...

Конструктор перемещения
#include &lt;iostream&gt; #include &lt;memory&gt; using namespace std; class A { int x; public: A(int _x) : x(_x){}; ...

Конструктор перемещения
Здравствуйте. У меня есть такой класс: class Organization { char *name; int year, staffQual; double salaryFund;

Для шаблонного класса перегрузить оператор присваивания, copy-конструктор, объекты cin и cout, оператор *
Помогите в следующем: Для класса шаблона следует перегрузить оператор присваивания, конструктор копирования, бинарный оператор суммы «*»,...


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

Или воспользуйтесь поиском по форуму:
34
Ответ Создать тему
Новые блоги и статьи
Контроль уникальности строк в табличной части документа
Maks 18.06.2026
Алгоритм из решения ниже разработан на примере нетипового документа "ПланированиеСпецтехники" с табличной частью "НаличиеОборудования", разработанного в КА2. Задача: контроль уникальности строк в. . .
Клиент
Uhbif79 18.06.2026
Здесь простой клиент для работы с сервером.
Сервер
Uhbif79 18.06.2026
Выкладываю простейший сервер.
Дефенестрация
kumehtar 18.06.2026
Узнал интересное слово. Дефенестрация. Это когда ты выбрасываешь кого-либо или что-либо из окна. Возьму на вооружение)))
Дихотомия добра и зла
kumehtar 18.06.2026
Как Дзен-буддисты говорят о добре и зле: не нужно воевать против зла, нужно воевать против невежества. Тогда добро станет ествественным, и поэтому вечным. Но дело в том, что невежество всё время. . .
Своя Интернет-Компания
iceja 18.06.2026
Я программист с экономическим образованием, пишу свой проект, это SaaS для бизнесов. Мне нужен co-founder с высшим экономическим образованием, и/ или инвестор. Сейчас проект в интенсивной разработке,. . .
24 Мат модель здравосохранения: функциональные требования к строительству пищеблока
anaschu 18.06.2026
СРесурсами1: финансовый SD-контур, калькулятор функциональных требований пищеблока Сегодня разделили затраты в агенте Экономика по образцу модели НАСОСЫ, добавили расчёт ROI и построили первый. . .
23. что сделано за последнее время.
anaschu 17.06.2026
• Эталон: Клиника НИИ питания РАМН, Москва — централизованный пищеблок, 225 коек, 180 пациентов • Git: репозиторий med2, ветка абсентеизм. Рабочий файл: СРесурсами1_v4. alp • Смежный проект:. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru