LINUX.ORG.RU

Надо ли помечать объект, если использовал конструктор переноса. с++

 


0

2

Добрый день, подскажите, когда я для объекта пишу конструктор переноса, мне нужно где-нибудь прописывать, что объект который переносится должен быть очищен, что бы деструктор не вызывался? А то в моем примере смотрю деструктор дважды вызывается.

#include <iostream>

using namespace std;
class A {
public:
    int *a;
    A() {
        cout << "A()" << endl;
        a = new int[10];
    }
    A(A &&rhs) {
        cout << " A(A &&rhs) "<< endl;
        a = rhs.a;


    }
    ~A() {
        cout <<"~A" << endl;
        delete []a;
    }
};
int main()
{
    A a;
    A b( move(a) );  cout << "Hello World!" << endl;
    return 0;
}



Последнее исправление: cetjs2 (всего исправлений: 1)

моем примере смотрю деструктор дважды вызывается

так ты два объекта создал, вот оба и деструктируются

Нет, не нужно никак помечать объект для системы/рантайма, если только для себя — чтобы потом понимать, что объект перенесён и в деструкторе не сделать фигни.

Bad_ptr ★★★★★
()

Деструктор будет вызываться в любом случае. Не вызывать его нельзя, потому что moved-from объект должен быть в valid, but unspecified state, а значит, требует должн быть корректо уничтожен.

Никаких «пометок» делать не нужно, достаточно в move-конструкторе занулять указатель в объекте, из которого совершается move.

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

А в деструктора проверять указатель затем на nullptr? Получается мне затем надо самому следить, что бы этот объект больше не вызывался (который обнулили).

da17
() автор топика
Ответ на: комментарий от da17

А в деструктора проверять указатель затем на nullptr?

da

Получается мне затем надо самому следить, что бы этот объект больше не вызывался (который обнулили).

da

Bad_ptr ★★★★★
()
Ответ на: комментарий от Bad_ptr

Чет как-то неудобно ((( придумали ведь всякие умные указатели и RAII, а тут как деды - следи за объектами.

da17
() автор топика
Ответ на: комментарий от da17

delete ptr; // не требуется проверка на nullptr - это встроено в delete

anonymous
()
Ответ на: комментарий от da17

говорят, чтобы не быть как диды, придумали Rust

Bad_ptr ★★★★★
()

Обнули указатель a.

ox55ff ★★★★★
()
Ответ на: комментарий от da17

придумали ведь всякие умные указатели и RAII, а тут как деды - следи за объектами.

Вот и используйте умные указатели, а не простые.

std::unique_ptr<int[]> a;
Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)
Ответ на: комментарий от da17

что бы этот объект больше не вызывался (который обнулили).

Можно использовать cppcheck или clang-tidy.

(который обнулили)

Дело в том, что объектом, из которого переместили содержимое, можно пользоваться. Например,

for (std::string buf; std::getline(buf);) {
    move_string_somewhere(std::move(buf));
}

каждую итерацию цикла осуществляет перемещение из buf – но пустая строка не становится «менее» строкой. Разумеется, в вашем случае не все так просто, но с некоторыми дополнительными усилиями (например, добавлением метода set_a(...), чтобы после каждого move давать объекту новый буфер) вы можете добиться схожих свойств.

Siborgium ★★★★★
()
Последнее исправление: Siborgium (всего исправлений: 1)

Такие вещи уметь надо, возьми unique_ptr. Если же настаиваешь и понимаешь зачем тебе это надо, то:

  1. Сноси обои с девками, ставь такие http://0x0.st/-gOE.png (смотри и пойми правую таблицу).

  2. Читать до просветления https://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom

Иначе ничего кроме очередного говнокода не выйдет.

kvpfs ★★
()
Ответ на: комментарий от da17

придумали ведь всякие умные указатели и RAII, а тут как деды - следи за объектами.

Что насоветовали про unique_ptr – гуд, если тебя не бомбит, что твой объект будет не на стеке, а в куче. А если бомбит, то я в своём приватном коде перемещаемые переменные называю с хвостовым подчёркиванием, чтобы только взглянув на имя, сразу начинать аккуратничать: e.g. void f(const string& s) { g(s); s.substr(...); } vs void f(string s_) { s_.substr(...); g(move(s_)); }.

dimgel ★★★★★
()

Вот поэтому меня и терзают смутные сомнения, а в друг и в правду rust когда-нибудь заборет c++?

Когда изучаешь rust, то даже мысли не возникает, а как там устроено перемещение объектов - настолько просто и естественно использовать его, а перемещение там вездесущее в языке - без него почти нельзя написать эффективный код. Уверен, что большинство растоманов даже понятия не имеют, как там устроено перемещение в языке, но это им нисколько не мешает писать быстрый код, используя это самое перемещение постоянно, не задумываясь.

А что имеем в c++? Далеко не каждый плюсовод понимает, как оно работает, а понимать надо, чтобы уметь использовать. Насколько все через одно место приделано! Ну, видно же, что в изначальном дизайне c++ не было и намека на то, что объекты могут быть эффективно перемещены в памяти (точнее, заголовки объектов, но не суть важно). И поэтому это все перемещение в c++ видится как кобыле пятая нога. Да еще постоянно этот std::move нужно писать и два амперсанда! Зачем?! Совместимость со старым кодом…

anonymous
()
Ответ на: комментарий от anonymous

Когда так хочется поговорить про раст но негде, оно понятно - за 2 месяца 4 темы, 3 в talks’ах, всё как у настоящего и живого языка )).

Когда изучаешь rust, то даже мысли не возникает, а как там устроено перемещение объектов

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

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

Нет, эффективный код - это передача объектов по указателю, перемещение дороже. Сейчас грепнул свой проект, 90% всех перемещений завязаны на интерфейс для юзера (который слинкуется с поделкой). Вот видит он какую-нибудь:

void lib_fn(vector<int> &)

и нет никакой гарантий для вызывающей стороны, что указатель там не припрячут. void lib_fn(vector<int>) с последующим move’ом однозначно говорит, что никакие указатели мне от юзера не нужны. А если линкую в исполняемый бинарь, то спокойно вообще без всяких мовов обойдусь.

Вообще все эти перемещения весьм органично вписались в плюсы.

kvpfs ★★
()
Ответ на: комментарий от anonymous

Вечно вам, долбоебам, хочется делать все так, чтобы и мыслей не возникало, не задумываясь.

anonymous
()
Ответ на: комментарий от anonymous

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

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

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

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

alysnix ★★★
()
Ответ на: комментарий от alysnix

обьекты грандиозных размеров

Это какие? Большинство жадных до памяти объектов жрут кучу, а сами довольно компактные. Вектор тот же, например.

ox55ff ★★★★★
()
Ответ на: комментарий от ox55ff

Большинство жадных до памяти объектов жрут кучу, а сами довольно компактные. Вектор тот же, например.

какая разница? если вы сделали копию, значит вы создали обьект подобный первому, но живущий свой жизнью. значит их можно независимо изменять, и «кучу жрать» они будут с двойной силой.

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 2)
Ответ на: комментарий от kvpfs

[quote]Нет, эффективный код - это передача объектов по указателю, перемещение дороже.[/quote]

Вообще-то, перемещение в С++ и есть по факту передача объекта по указателю))

anonymous
()
Ответ на: комментарий от anonymous

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

anonymous
()
Ответ на: комментарий от anonymous

Вообще-то, перемещение в С++ и есть по факту передача объекта по указателю))

Ясно, зови следующего.

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

Всё намешал и Strong exception safety вплёл. Это лишь говорит о продуманности стандартной либлы. Пиши свою No exception safety полностью, желательно где-нибудь в Расте, а у нас так.

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

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от kvpfs

В переводе на обычный язык: «нам не нужен человеческий мув, потому что мув используется редко в C++?» Это вам не тут. Так?

anonymous
()
Ответ на: комментарий от anonymous

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

И обёртки пишут не рукожопые ребята и размещают их в стд. Нужно расслабиться и пользоваться ими, лишь иногда бывает нужно запретить объекту копироваться и перемещаться или копироваться, например. В редком проекте нужно писать свои реализации конструкторов и операторов=, думаю, что ты бы знал, если бы писал хоть что-то.

kvpfs ★★
()
Ответ на: комментарий от kvpfs

Увы, именно их и приходится писать, потому тот же std::shared_ptr совершенно не годится для однопоточного скоростного исполнения при частом использовании, а с этим мувом даже в случае умных указателей есть нюансы, из-за которых скорость исполнения отличается чуть ли не на 50%-100% из-за тонких особенностей реализации в зависимости от выбранного компилятора. Вот, перед создателями gcc снимаю шляпу.

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

anonymous
()
Ответ на: комментарий от anonymous

Вообще-то, перемещение в С++ и есть по факту передача объекта по указателю))

Нет.

Siborgium ★★★★★
()
Ответ на: комментарий от anonymous

Увы, именно их и приходится писать, потому тот же std::shared_ptr совершенно не годится для однопоточного скоростного исполнения при частом использовании

Ты там, наверное, голые циклы с бедным шаред_птр’ом гоняешь раз для тебя критично инкрементнуть атомик ). Хорошо, что не стали плодить помойку в стд под сингл/мульти-тред. Твой случай маргинальный, надо искать на стороне что-то. Я в своей поделке даже какого-то влияния std::function в многопроходном цикле заметить не мог (а это аллокация в куче).

Раз тут речь и про rust, мне нравится одна особенность этого языка. Там идиоматический код работает быстро, какую фичу ни возьми.

Подозреваю, что там высокоуровневые костыли много тормознее плюсовых шаред_птров, всякие каналы для ITC (и бог его знает что там под капотом), тогда как в тех же плюсах ты можешь прозрачно записать в атомик(релаксед).

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от kvpfs

В расте для однопоточной умной ссылки (Rc) не используются атомики. При муве счетчик не затрагивается (там просто делается memmove заголовка объекта). При деструкторе мы точно (для строгой ссылки) знаем, что счетчик нужно уменьшить, чтобы проверить, что ссылка не опустела, т.е. инлайнить в вызываемый код никакую проверку на nullptr точно не надо (см. примечание внизу). В разделяемой ячейке находится сам ссылаемый объект, а не указатель на него, что уменьшает уровень косвенности на единицу при обращении - выше локальность кеша, меньше обращений к памяти.

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

Нет, я не хочу обобщать. Есть случаи, где для C++ что-то можно сделать эффективнее. Та же семантика перемещения в C++ и rust сильно отличается в деталях. В C++ ты легко можешь переместить объект из контейнера, не удаляя из самого контейнера, тогда как в rust нужно именно что вывести объект из под контейнера явно.

Просто, честно говоря, я уже так намаялся с C++, пытаясь получить сопоставимую производительность с растом. В целом, мне это удалось, а в одном очень важном для меня классе задач даже плюсы обгоняют раст.

Однако, на расте мне писать проще. Может быть, у кого-то будет совершенно другой опыт. И я замечаю, что далеко не только мне писать на расте проще. Чем моложе программисты, тем таких больше. Честно говоря, даже не знаю, увлекается ли молодежь плюсами сейчас?

Примечание. В C++ такой инлайнинг то дает выигрыш в случае мува, то дает проигрыш в случае копирования

anonymous
()
Ответ на: комментарий от anonymous

Просто, честно говоря, я уже так намаялся с C++, пытаясь получить сопоставимую производительность с растом.

а что хоть за задача-то? откуда нужна такая бешеная производительность, что даже плюсы не осилили ее?

alysnix ★★★
()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от anonymous

моделирование, один из видов

и какой вид конкретно?.. у меня есть серьезные сомнения, что плюсы проиграют кому бы то ни было… при правильном использовании конечно…без всяких там шаред поинтеров и прочих наворотов из стандартной либы. моделирования надо писать без них точно.

alysnix ★★★
()
Ответ на: комментарий от anonymous

К слову - зашёл на гитхаб и сразу нашёл однопоточный шаред_птр https://github.com/SRombauts/shared_ptr.

В расте для однопоточной умной ссылки (Rc) не используются атомики. При муве счетчик не затрагивается (там просто делается memmove заголовка объекта). При деструкторе мы точно (для строгой ссылки) знаем, что счетчик нужно уменьшить, чтобы проверить, что ссылка не опустела, т.е. инлайнить в вызываемый код никакую проверку на nullptr точно не надо (см. примечание внизу). В разделяемой ячейке находится сам ссылаемый объект, а не указатель на него, что уменьшает уровень косвенности на единицу при обращении - выше локальность кеша, меньше обращений к памяти.

Я не проверял производительность (не интересно было копаться в расте), но звучит это всё как какой-то лютый костыль через задницу - ввиду отсутствия указателей приходится копировать объекты «по честному», а не ходить по ссылкам. В случае каких-то скаляров простых это может и круто всё и косвенность действительно меньше, но вот очень скоро (когда данные станут размеров в несколько интов) такой подход начнёт сливать. В общем красиво на каких-то игрушечных примерах - мол смотрите, у меня здесь шаред_птр с одним интом внутри и я рву плюсы.

Да даже на скалярах сомневаюсь - всё равно надо сходить куда-то и скопировать. Да и всякие оптимизации с накоплением в регистрах никто не отменял, т.е. если цикл какой-то, то плюсовому хелоу ворлду не нужно 100500 раз ходить куда-то.

kvpfs ★★
()
Последнее исправление: kvpfs (всего исправлений: 1)
Ответ на: комментарий от kvpfs

ввиду отсутствия указателей приходится копировать объекты «по честному», а не ходить по ссылкам.

В rust есть и ссылки и указатели.

anonymous
()
Ответ на: комментарий от anonymous

В rust есть и ссылки и указатели

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

string s = "hello world";
string &rs = s;
string_store ss(move(s)); // s == "";
s = ss.release_string();
cout << rs; // cout: hello world

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

kvpfs ★★
()
Ответ на: комментарий от anonymous

Да еще постоянно этот std::move нужно писать и два амперсанда! Зачем?! Совместимость со старым кодом…

Ты лучше посчитай, количество букв в таких кейсах - оказывается, при предаче мутаблеьной ссылки нужно дважды прописать «mut», а для копирования объектов нужно написать Clone trait, а потом ниже по коду

my_object_that_represent_something_and_litle_more.Clone();

Совместимость со старым кодом? Не думаю, наверное, чтобы ты просто страдал.

kvpfs ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.