Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.91/11: Рейтинг темы: голосов - 11, средняя оценка - 4.91
13 / 11 / 3
Регистрация: 22.11.2013
Сообщений: 127

std::unique ведет себя не так как ожидалось

12.08.2021, 17:30. Показов 2383. Ответов 9
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
win 10, visual studio 19.
У меня есть два контейнера с указателями на std::string
Code
1
2
std::deque<std::string*> deq;
std::deque<std::string*> deq2;
Я хочу их обьеденить и если там есть указатели на одинаковые строки - удалить дубли. Использую std::unique для определения дублей. std::unique должен элемент с дублем перенести в конец контейнера. но у меня он почему-то не переносит этот элемент, а копирует его.
Содержимое контейнера до вызова std::unique

Содержимое контейнера после вызова std::unique

Откуда там взялось по два элемента с одинаковым адресом?
Что я делаю не так?
От мой код
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
// первая коллекция
std::deque<std::string*> deq; 
    for (int i = 0; i < 5; ++i)
    {
        deq.push_back(new std::string("string " + std::to_string(i)));
    }
    // вторая коллекция
    std::deque<std::string*> deq2; 
    for (int i = 0; i < 5; ++i)
    {
        deq2.push_back(new std::string("string " + std::to_string(i)));
    }
    // соединяю обе коллекции в кучу
    for (auto i : deq2)
        deq.push_back(i);  
    // сортирую элементы коллекции по значению в строке
    std::sort(deq.begin(), deq.end(), [](const std::string* a, const std::string* b)
        {
            return *a < *b;  
        });
    // переношу элементы коллекции в конец контейнера и получаю указатель на место с дублями
    auto result = std::unique(deq.begin(), deq.end(), [](const std::string* a, const std::string* b)
        {
            return *a == *b; 
        });
    // освобождаю память занимаемую дублями
    for (auto it = result; it != deq.end(); ++it)
        delete* it; 
    // очищаю коллекцию от удаленных указателей
    deq.erase(result, deq.end()); 
    // получаю вылет (обращение к удаленному указателю)
    for (auto i : deq)
        std::cout << *i << std::endl;
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
12.08.2021, 17:30
Ответы с готовыми решениями:

Функция ведет себя не так, как планировалось
#include &lt;iostream&gt; const int Seasons = 4; const char* Snames = {&quot;Spring&quot;, &quot;Summer&quot;, &quot;Fall&quot;, &quot;Winter&quot;}; void Fill(double*...

Std::forward_list по-разному ведет себя с компиляторами gcc и vc++
Есть такой код: #include &lt;iostream&gt; #include &lt;iomanip&gt; #include &lt;forward_list&gt; using namespace std; void show(const...

Нюансы работы с массивами: почему программа ведёт себя не так, как ожидается?
// ConsoleApplication20.cpp: определяет точку входа для консольного приложения. // #include &quot;stdafx.h&quot; #include...

9
"C with Classes"
2022 / 1404 / 523
Регистрация: 16.08.2014
Сообщений: 5,885
Записей в блоге: 1
12.08.2021, 18:11
Цитата Сообщение от Bino321 Посмотреть сообщение
Откуда там взялось по два элемента с одинаковым адресом?
скорее всего это деталь реализации, и тебя она не должна волновать, главное что тебя должно волновать, так это то что алгоритм unique возвращает итератор указывающий за последний элемент новой последовательности.

Relative order of the elements that remain is preserved and the physical size of the container is unchanged. Iterators in [r, last) (if any), where r is the return value, are still dereferenceable, but the elements themselves have unspecified values. A call to unique is typically followed by a call to a container's erase member function, which erases the unspecified values and reduces the physical size of the container to match its new logical size.

Полезная ссылка.
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13201 / 6836 / 1822
Регистрация: 18.10.2014
Сообщений: 17,294
12.08.2021, 18:34
Цитата Сообщение от Bino321 Посмотреть сообщение
Откуда там взялось по два элемента с одинаковым адресом?
Ну так std::unique не обещает и никогда не обещала вам, что она будет выполнять некое разделение и переупорядочение элементов на "уникальные" и "не уникальные". std::unique не выполняет переупорядочения элементов с сохранением уникальности исходных значений. std::unique просто тупо копирует/перемещает содержимое уникальных элементов вперед - к началу списка - не беспокоясь при этом о том, чтобы куда-то сохранить старое значение из начала списка. Все.

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

Вот здесь вы можете посмотреть "возможные реализации" std::unique: https://en.cppreference.com/w/cpp/algorithm/unique

Например, если вы на вход std::unique дадите 1 1 2, то на выходе вы получите 1 2 2. Как видите, одна из 1 пропала, а 2 - размножилась. std::unique не беспокоится о том, чтобы сохранить исходный "состав" вашей последовательности неизменным. Такой задачи перед std::unique не ставится.
0
13 / 11 / 3
Регистрация: 22.11.2013
Сообщений: 127
12.08.2021, 18:41  [ТС]
Цитата Сообщение от _stanislav Посмотреть сообщение
скорее всего это деталь реализации, и тебя она не должна волновать
Это меня какраз очень волнует, потому что смотрите что происходит: обратите внимание на этот скрин
Содержимое контейнера после вызова std::unique

В даном примере std::unique возвращает итератор на элемент шестой элемент коллекции (deq[5]). Начиная с этого элемента - это пошли дубли. Так? Я начинаю удалять дубли, но так как это указатели - мне нужно сразу перед удалением их с контейнера освободить память на которую они указывают чтобы небыло утечки памяти. Но обратите внимание что теперь, после работы std::unique, в элементов deq[3] и deq[6] почему-то один и тот же адрес. И когда я освобождаю память для дубля( deq[6]) - я этим самим удаляю и нужную информацию (не дубля) из deq[3]
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13201 / 6836 / 1822
Регистрация: 18.10.2014
Сообщений: 17,294
12.08.2021, 18:48
Цитата Сообщение от Bino321 Посмотреть сообщение
Но обратите внимание что теперь, после работы std::unique, в элементов deq[3] и deq[6] почему-то один и тот же адрес.
А почему бы и нет? В какой-то момент в процессе "упаковки" выполнилось deq[3] = deq[6] - и вот готовы одинаковые значения указателей. Почему вас это удивляет?

Цитата Сообщение от Bino321 Посмотреть сообщение
Начиная с этого элемента - это пошли дубли.
Нет, никакие "дубли" там не пошли. Начиная с этого элемента пошли неспецифицированные значения, которые можно лишь безопасно деструктировать или переназначить, но к остаточному содержимому которых обращаться бесполезно.

Можно вообще вычеркнуть мои объяснения из предыдущего сообщения, а просто формально запомнить, что содержимое диапазона [result, deq.end()) после выполнения std::unique неспецифицированно. Ни о каком применении delete *it к этому диапазону не может быть и речи вообще.
1
13 / 11 / 3
Регистрация: 22.11.2013
Сообщений: 127
12.08.2021, 18:51  [ТС]
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
std::unique не беспокоится о том, чтобы сохранить исходный "состав" вашей последовательности неизменным. Такой задачи перед std::unique не ставится.
Понятно. Выходит std::unique нельзя использовать для контейнера с указателями и при этом сравнивать их не по адресу а по значению?
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13201 / 6836 / 1822
Регистрация: 18.10.2014
Сообщений: 17,294
12.08.2021, 20:26
Цитата Сообщение от Bino321 Посмотреть сообщение
Выходит std::unique нельзя использовать для контейнера с указателями и при этом сравнивать их не по адресу а по значению?
Если это - голые указатели, которые владеют указываемой памятью (как в вашем случае), то - нельзя. Дело закончится утечками памяти или неопределенным (множественным) владением.
0
зомбяк
 Аватар для TRam_
1585 / 1219 / 345
Регистрация: 14.05.2017
Сообщений: 3,940
13.08.2021, 12:03
Bino321, переделал на умные указатели:

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
    // первая коллекция
    std::deque<std::shared_ptr<std::string>> deq;
    for (int i = 0; i < 5; ++i)
    {
        deq.push_back(std::make_shared<std::string>(std::string("string " + std::to_string(i))));
    }
    // вторая коллекция
    std::deque< std::shared_ptr<std::string>> deq2;
    for (int i = 0; i < 5; ++i)
    {
        deq2.push_back(std::make_shared<std::string>(std::string("string " + std::to_string(i))));
    }
    // соединяю обе коллекции в кучу
    for (auto i : deq2)
        deq.push_back(i);
    // сортирую элементы коллекции по значению в строке
    std::sort(deq.begin(), deq.end(), [](const std::shared_ptr<std::string> a, const std::shared_ptr<std::string> b)
        {
            return *a < *b;
        });
    // переношу элементы коллекции в конец контейнера и получаю указатель на место с дублями
    auto result = std::unique(deq.begin(), deq.end(), [](const std::shared_ptr<std::string> a, const std::shared_ptr<std::string> b)
        {
            return *a == *b;
        });
    // очищаю коллекцию от удаленных указателей
    deq.erase(result, deq.end());
 
    for (auto i : deq)
        std::cout << *i << std::endl;
и не надо беспокоиться о вылетах. Пока указатель где-то остался неудалённым, данные удаляться не будут.
0
 Аватар для Kuzia domovenok
4268 / 3327 / 926
Регистрация: 25.03.2012
Сообщений: 12,536
Записей в блоге: 1
13.08.2021, 13:12
Цитата Сообщение от Bino321 Посмотреть сообщение
// освобождаю память занимаемую дублями
    for (auto it = result; it != deq.end(); ++it)
        delete* it;
зачееем???
Если дубли дублируют не только значения по указателю, но и сами указываемые адреса естесственно будут проблемы.
Вы освобождаете память, на которую ссылаются ещё несколько указателей. Не надо так.

Добавлено через 1 минуту
Цитата Сообщение от Bino321 Посмотреть сообщение
Понятно. Выходит std::unique нельзя использовать для контейнера с указателями и при этом сравнивать их не по адресу а по значению?
да всё можно, надо только понимать, что происходит, что ты делаешь, а что тебе на самом деле нужно.

Добавлено через 4 минуты
Я как вариант предлагаю делать в 2 прохода.
Первый std::unique перемещает в хвост элементы с повторяющимися адресами
C++
1
2
3
4
5
6
7
8
9
    std::sort(deq.begin(), deq.end(), [](const std::string* a, const std::string* b)
        {
            return a < b;  
        });
    // переношу элементы коллекции в конец контейнера и получаю указатель на место с дублями
    auto result = std::unique(deq.begin(), deq.end(), [](const std::string* a, const std::string* b)
        {
            return a == b; 
        });
их просто ирейзим.
C++
1
  deq.erase(result, deq.end());
Первый std::unique перемещает в хвост элементы с повторяющимися адресами
C++
1
2
3
4
5
6
7
8
9
    std::sort(deq.begin(), deq.end(), [](const std::string* a, const std::string* b)
        {
            return *a < *b;  
        });
    // переношу элементы коллекции в конец контейнера и получаю указатель на место с дублями
    auto result = std::unique(deq.begin(), deq.end(), [](const std::string* a, const std::string* b)
        {
            return *a == *b; 
        });
их ирейзим и освобождаем память.

C++
1
2
3
4
5
    // освобождаю память занимаемую дублями
    for (auto it = result; it != deq.end(); ++it)
        delete* it; 
    // очищаю коллекцию от удаленных указателей
    deq.erase(result, deq.end());
0
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13201 / 6836 / 1822
Регистрация: 18.10.2014
Сообщений: 17,294
13.08.2021, 18:52
Цитата Сообщение от Kuzia domovenok Посмотреть сообщение
Первый std::unique перемещает в хвост элементы с повторяющимися адресами
Тут, как говорится, хоть кол на голове теши.... std::unique никогда ничего в хвост не перемещает, как было ясно объяснено выше.

Добавлено через 3 минуты
Цитата Сообщение от TRam_ Посмотреть сообщение
переделал на умные указатели:
Здесь совершенно не нужен std::shared_ptr. Все указатели уникальны и с задачей прекрасно справляется std::unique_ptr.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
13.08.2021, 18:52
Помогаю со студенческими работами здесь

Нюансы работы с оператором ветвления if else: почему программа ведёт себя не так, как ожидается?
Имеется код: int a; char q; for (;;) { cout &lt;&lt; &quot;Введите число: &quot;; cin &gt;&gt; a;

Нюансы работы с оператором ветвления if else: почему программа ведёт себя не так, как ожидается?
#include &lt;iostream&gt; #include &lt;string&gt; using namespace std; void check_weekday(string day) { if (day == &quot;Понедельник&quot; || day ==...

Почему IE так себя ведет?
У меня сайт на движке wordpress в браузерах Opera и Mozilla все работает отлично, а вот в IE сам сайт открывается а когда переходишь на...

Почему TButton ведет себя не так
Всем здраствуйте.Код трудно привести по причине его размеров,но если кто нибудь просто имеет какие - нибудь соображения - поделитесь...

Почему DHCP ведет себя так?
Добрый день, В здании несколько организаций, но Интернет общий, настроил Mikrotik RB951G-2HnD таким образом, что на каждом сетевом...


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

Или воспользуйтесь поиском по форуму:
10
Ответ Создать тему
Новые блоги и статьи
My Business CRM
MaGz GoLd 16.04.2026
Всем привет, недавно возникла потребность создать CRM, для личных нужд. Собственно программа предоставляет из себя базу данных клиентов, в которой можно фиксировать звонки, стадии сделки, а также. . .
Знаешь почему 90% людей редко бывают счастливыми?
kumehtar 14.04.2026
Потому что они ждут. Ждут выходных, ждут отпуска, ждут удачного момента. . . а удачный момент так и не приходит.
Фиксация колонок в отчете СКД
Maks 14.04.2026
Фиксация колонок в СКД отчета типа Таблица. Задача: зафиксировать три левых колонки в отчете. Процедура ПриКомпоновкеРезультата(ДокументРезультат, ДанныеРасшифровки, СтандартнаяОбработка) / / . . .
Настройки VS Code
Loafer 13.04.2026
{ "cmake. configureOnOpen": false, "diffEditor. ignoreTrimWhitespace": true, "editor. guides. bracketPairs": "active", "extensions. ignoreRecommendations": true, . . .
Оптимизация кода на разграничение прав доступа к элементам формы
Maks 13.04.2026
Алгоритм из решения ниже реализован на нетиповом документе, разработанного в конфигурации КА2. Задачи, как таковой, поставлено не было, проделанное ниже исключительно моя инициатива. Было так:. . .
Контроль заполнения и очистка дат в зависимости от значения перечислений
Maks 12.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "ПланированиеПерсонала", разработанного в конфигурации КА2. Задача: реализовать контроль корректности заполнения дат назначения. . .
Архитектура слоя интернета для сервера-слоя.
Hrethgir 11.04.2026
В продолжение https:/ / www. cyberforum. ru/ blogs/ 223907/ 10860. html Знаешь что я подумал? Раз мы все источники пишем в голове ветки, то ничего не мешает добавить в голову такой источник, который сам. . .
Подстановка значения реквизита справочника в табличную часть документа
Maks 10.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "ПланированиеПерсонала", разработанного в конфигурации КА2. Задача №1: при указании работ (справочник РаботыПоРемонтуСпецтехники),. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru