Елы-палы, чел, помогай тебе всевышний! Я, видимо, не с того начал (Майерс, страусиный труп). После первых прочтений было понятно чуть больше, чем нихера. А тут как по маслу. Очень хорошо объяснил. Спасибо!
На 11:00 разве не конструктор копирования вызывается? У нас же на момент возврата из функции объект Array a еще не проинициализирован, поэтому мы его создаем из объекта temp, возвращенного функцией, для чего вызываем конструктор копирования. Оператор присваивания вызовется в следующем случае: Array a(1); a = createBigArray(10);
Если не считать, что то, что он понаписал - на самом деле не работает так, как он рассказывает. Пошаговая отладка четко это показывает. Автор тут накосяпорил конкретно так.
Чето тут не то, тут есть косяк - перемещающий функционал не вызывается при создании объекта таким способом: Array a = Create(1000); Оператор = перемещения вызывается в этом случае: Array a(10); a = Create(10000); В общем, чето тут как то не то.
В видео очень не хватает примеров как эту же задачу можно решить другими способами, например передачей new Array из createBigArray() через указатель, а то создаётся впечатление что проблему кроме как move семантикой ни как не решить. Ну и сравнительный анализ хоть небольшой: чем удобнее, когда использовать какой способ
К примеру в embedded программировании бывает нужно избегать аллокации памяти, и move-семантика может в этом помочь. Хотя даже в этом случае есть другие методы, например, использовать наследование. Либо в рамках задачи можно создать Array и передать его на заполнение в fillRandomData()
Не, реально, какая то фигня получается. Чувак, вот ты сам проверь в пошаговой отладке то, чего понаписал то. Ну не работает твоя писанина так, как ты рассказываешь. Я лично проверил в разных компиляторах во всевозможных вариантах - не так это работает. Кароч, чувак, ты чето здорово накосяпорил. И если ты - препод в Бауманке, то "это фиаско, братан"(С). Реально, чувак, тут какая-то ошибка. Если в Бауманке преподы не разбираются в предмете, то о каком "прорыве" в шаРашке нам твердят по телику? Это ваще полный ахтунг чето.
"Ни че не понял, но очень интересно"...))) А, кто мешает передавать по указателю???????????????................... Да и ссылка это всего лишь символьный псевдоним адреса для компилятора. Все переменные по логике происходящего это ссылки. В принципе, отличия ссылки от указателя в том, что ссылка всегда имеет один и тот же адрес, а указатель может иметь любой адрес, но все они типизированные псевдонимы, с отсчетом ячейки по размеру типа, и понимаются только компилятором. Короче пока программа в текстовом редакторе, то у тебя есть и R и L, а после компиляции они все просто по адресу в ячейке...))
Попробуйте получить указатель на результат вычисления выражение вида например (a+b). Вы совершенно правы, когда пишете, что после компиляции данные всегда окажутся в ячейке с некоторым адресом. Но до компиляции можно получить указатель или классическую Lvalue-ссылку на именованную переменную, а не на результат вычисления выражения. Появление Rvalue-ссылок как раз призвано решить эту проблему. И вслед за этим появляются качественно новые решения, основанные на семантике переноса (перемещения) - особые типы конструкторов копирования и операторов присваивания. Их применение позволяет избежать избыточных операций копирования данных из одних буферов в другие. А обычные Lvalue-ссылки действительно являются лишь "синтаксическим сахаром", обёртками над указателями.
@@ria-bmstu РАЗВЕРНИ СООБЩЕНИЕ, в самом конце ответ на &(a+b) = new int(a + b); Не, ну это и понятно, значения а и b, лежат в двух разных ячейках, у которых разные адреса, по этому такое не прокатит: int* foonc(int a, int b) { return &(a+b); } А, такое прокатывает: int* foonc(int a, int b) { return &(a); } Хотя я думаю, что СКОБКИ ( ... ) должны быть определяющими ячейку результата, ведь просто результат (a+b) я могу вернуть, следовательно, казалось бы указатель на результат тоже должно быть возможным вернуть. Результат вернуть можно: int fooncI(int a, int b) { return (b + a); } Все потому, что ячейка результата временная, даже если я верну указатель ячейки созданной в функции, она все равно временная, и там может оказаться все что угодно, следовательно, результат нужно фиксировать к примеру в глобальном указателе, это логично. Для того чтоб зафиксировать память существует оператор выделения памяти NEW, по этому при возврате указателя достаточно добавить этот оператор и тип возвращаемого отсчета памяти: int* fooncP(int a, int b) { return new int(a + b); } А, если ты еще и присвоишь ссылочный псевдоним, то и освобождать память не нужно будет, я так думаю, так как все переменные являются ссылками: Использование: int* pc = fooncP(5, 6); //вызываю функцию и получаю указатель на память. int& cc = *pc; //присваиваю памяти псевдоним. std::cout
@@ria-bmstu А вот вкратце указатель на (a+b): int* fooncP(int a, int b) { return new int(a + b); //чтоб вернуть указатель на результат, нужно ячейку зафиксировать //оператором выделения памяти NEW, иначе, так как функция временный объект то и возвращаемое //значение функцией находится во временной памяти, которая освободится после использования //функции, и тем самым по адресу в ячейке может оказаться все что угодно. }
@@ria-bmstu в чистом C без труда работают через указатели, правда там несколько иной стиль кода. В C возвращаемое значение функции - это часто не нужное нам значение, а код ошибки (0 если всё норм), а результат выполнения функции - это дополнительный указатель в качестве входного параметра, говоря проще int foo(int* input, int* output) {/*code*/}; //output - это то это результат выполнения, который нам интересен, а return - это результат успешности выполнения функции (если функция однозначна можно было бы использовать void, но по привычке все используют int). То что в итоге нагородили в C++ - это безумие, которое не только интуитивно не понимают даже "как бы" продвинутые разработчики, но и легко создать трудноуловимые ошибки, при этом ещё и количество строк кода резко увеличивается решая исключительно формальную обслуживающую задачу, а единственный профит от этого - это то, что класс потом можно будет использовать эффективно, однако если потребуется что-то поменять то в C-стиле можно переписать несколько строк и всё без труда заработает, а в C++ rvalue-ссылками придётся следить чтобы нигде ничего не сломать. +Ещё выбрали обозначение идиотское (&&) и тоже не интуитивное (по логике это как взятие адреса у адреса), обычные ссылки имеют логическую подоплёку, т.к. & символизирует "адресность", что символизирует в этом контексте && - чёрт его знает, просто "потому что потому". В результате получается так, что даже в C++ чаще можно встретить работу без копирования на обычных указателях в стиле C вместо танцев на граблях с move-семантикой в стиле Rust. Кстати почти никто из "знатоков" move семантики не рассказывает что именно происходит с памятью, большинство я уверена даже сами не знают :), и вообще move-семантику если и изучать/преподавать, то лучше отвлечься от кода и нарисовать области памяти и на таком примере пояснить что именно мы делаем, так заодно сразу же всплывёт разница между lvalue и rvalue ссылками, а видео, в целом, неплохое, лучше чем у многих.
В общем, поковырявшись самостоятельно, выяснил, что не так всё это работает, как тут рассказано. Перегрузка оператора присваивания для перемещения будет работать только если уже существующему объекту класса будут присваиваться другие значения. То есть, не при первоначальном создании объекта, как тут показано, а при изменении существующего объекта посредством оператора присваивания. Вот блин, ну и "препод", ё-мое. Походу, он сам толком не знает, как конкретно это работает. Так-с, щас надо разобраться с тем, что понаписано в самих реализациях конструкторов и операторов, там чето тоже лажа какая-то стопудово чето. Кароч, втопку таких горе-преподов, сжигать на костре инквизиции их надобно...
Кароч, детально разобравшись в вопросе, подытожу - автор видоса допустил принципиальную ошибку в использовании перемещающего оператора и вообще в понимании всего процесса работы. Перемещающий оператор присваивания не будет вызываться при создании нового объекта класса Array a = Create(); Он будет вызываться только для УЖЕ СОЗДАННОГО объекта. Ну и в показанном примере все что написано, в принципе не нужно, работает и без него без каких-либо проблем. Прискорбно, что сей горе-препод из Бауманки пытается учить других, при этом сам не понимает, как тут реально работает. Ему всего лишь нужно было запустить пошаговую отладку и проследить за исполнением кода. Он наверно потому и не стал запускать отладку, что она покажет совсем не то, про что рассказывает препод. Печально конечно. Сей бодячий фейл показывает истинное состояние дел в нашерашке в сфере ИТ, в противовес тому, что сыплется на нас с зомбоящика.
@@sashatim8244 тогда сразу же нашел отличное объяснение, но естественно, как всегда, в англоязычном сегменте. А что мне учить без вас разберусь. А проблема отечественных «учителей» она тянется еще с незапамятных времен. Любые книги, учебники, видеоуроки убоги за очень редким исключением. Это касается не только ЯП, а вообще всего.
Самое адекватное объяснение этой темы. А то читал литературу и там так все запутанно объясняют)Спасибо !!!!!
этот комент я пишу в 2023 году 19.03 и этот ролик по r-value самый офигенный и понятный . Автор спасибо тебе из 2023 💜
О! Очень хорошо объясняете, четко понятно, отличная дикция, спасибо!
Спасибо! Отличное объяснение
Елы-палы, чел, помогай тебе всевышний! Я, видимо, не с того начал (Майерс, страусиный труп). После первых прочтений было понятно чуть больше, чем нихера. А тут как по маслу. Очень хорошо объяснил. Спасибо!
На 11:00 разве не конструктор копирования вызывается? У нас же на момент возврата из функции объект Array a еще не проинициализирован, поэтому мы его создаем из объекта temp, возвращенного функцией, для чего вызываем конструктор копирования.
Оператор присваивания вызовется в следующем случае:
Array a(1);
a = createBigArray(10);
Отличное объяснение. Спасибо
Хорошее объяснение, спасибо!
Супер
Спасибо за хорошее обьяснение)
Как будто настоящего профессора слушаешь, да ещё всё так хорошо объясняется
Если не считать, что то, что он понаписал - на самом деле не работает так, как он рассказывает. Пошаговая отладка четко это показывает. Автор тут накосяпорил конкретно так.
Спасибо, понял.
Чето тут не то, тут есть косяк - перемещающий функционал не вызывается при создании объекта таким способом: Array a = Create(1000); Оператор = перемещения вызывается в этом случае: Array a(10); a = Create(10000); В общем, чето тут как то не то.
есть хорошая книжка на данную тему: "C++ Move Semantics The Complete Guide" автора Nicolai M. Josuttis. рекомендую
В видео очень не хватает примеров как эту же задачу можно решить другими способами, например передачей new Array из createBigArray() через указатель, а то создаётся впечатление что проблему кроме как move семантикой ни как не решить. Ну и сравнительный анализ хоть небольшой: чем удобнее, когда использовать какой способ
К примеру в embedded программировании бывает нужно избегать аллокации памяти, и move-семантика может в этом помочь. Хотя даже в этом случае есть другие методы, например, использовать наследование. Либо в рамках задачи можно создать Array и передать его на заполнение в fillRandomData()
Тема rvalue-ссылок не раскрыта.
Более того, написанный код - ошибочный, я проверил самолично, он не работает так, как рассказывает автор.
Не, реально, какая то фигня получается. Чувак, вот ты сам проверь в пошаговой отладке то, чего понаписал то. Ну не работает твоя писанина так, как ты рассказываешь. Я лично проверил в разных компиляторах во всевозможных вариантах - не так это работает. Кароч, чувак, ты чето здорово накосяпорил. И если ты - препод в Бауманке, то "это фиаско, братан"(С). Реально, чувак, тут какая-то ошибка. Если в Бауманке преподы не разбираются в предмете, то о каком "прорыве" в шаРашке нам твердят по телику? Это ваще полный ахтунг чето.
"Ни че не понял, но очень интересно"...))) А, кто мешает передавать по указателю???????????????................... Да и ссылка это всего лишь символьный псевдоним адреса для компилятора. Все переменные по логике происходящего это ссылки. В принципе, отличия ссылки от указателя в том, что ссылка всегда имеет один и тот же адрес, а указатель может иметь любой адрес, но все они типизированные псевдонимы, с отсчетом ячейки по размеру типа, и понимаются только компилятором. Короче пока программа в текстовом редакторе, то у тебя есть и R и L, а после компиляции они все просто по адресу в ячейке...))
Попробуйте получить указатель на результат вычисления выражение вида например (a+b). Вы совершенно правы, когда пишете, что после компиляции данные всегда окажутся в ячейке с некоторым адресом. Но до компиляции можно получить указатель или классическую Lvalue-ссылку на именованную переменную, а не на результат вычисления выражения. Появление Rvalue-ссылок как раз призвано решить эту проблему. И вслед за этим появляются качественно новые решения, основанные на семантике переноса (перемещения) - особые типы конструкторов копирования и операторов присваивания. Их применение позволяет избежать избыточных операций копирования данных из одних буферов в другие. А обычные Lvalue-ссылки действительно являются лишь "синтаксическим сахаром", обёртками над указателями.
@@ria-bmstu РАЗВЕРНИ СООБЩЕНИЕ, в самом конце ответ на &(a+b) = new int(a + b);
Не, ну это и понятно, значения а и b, лежат в двух разных ячейках, у которых разные адреса, по этому
такое не прокатит:
int* foonc(int a, int b)
{
return &(a+b);
}
А, такое прокатывает:
int* foonc(int a, int b)
{
return &(a);
}
Хотя я думаю, что СКОБКИ ( ... ) должны быть определяющими ячейку результата, ведь просто результат (a+b) я могу вернуть, следовательно, казалось бы указатель на результат тоже должно быть возможным вернуть.
Результат вернуть можно:
int fooncI(int a, int b)
{
return (b + a);
}
Все потому, что ячейка результата временная, даже если я верну указатель ячейки созданной в функции, она все равно временная, и там может оказаться все что угодно, следовательно, результат нужно фиксировать к примеру в глобальном указателе, это логично.
Для того чтоб зафиксировать память существует оператор выделения памяти NEW, по этому при возврате указателя достаточно добавить этот оператор и тип возвращаемого отсчета памяти:
int* fooncP(int a, int b)
{
return new int(a + b);
}
А, если ты еще и присвоишь ссылочный псевдоним, то и освобождать память не нужно будет, я так думаю, так как все переменные являются ссылками:
Использование:
int* pc = fooncP(5, 6); //вызываю функцию и получаю указатель на память.
int& cc = *pc; //присваиваю памяти псевдоним.
std::cout
@@ria-bmstu А вот вкратце указатель на (a+b):
int* fooncP(int a, int b)
{
return new int(a + b); //чтоб вернуть указатель на результат, нужно ячейку зафиксировать
//оператором выделения памяти NEW, иначе, так как функция временный объект то и возвращаемое
//значение функцией находится во временной памяти, которая освободится после использования
//функции, и тем самым по адресу в ячейке может оказаться все что угодно.
}
@@ria-bmstu в чистом C без труда работают через указатели, правда там несколько иной стиль кода. В C возвращаемое значение функции - это часто не нужное нам значение, а код ошибки (0 если всё норм), а результат выполнения функции - это дополнительный указатель в качестве входного параметра, говоря проще
int foo(int* input, int* output) {/*code*/}; //output - это то это результат выполнения, который нам интересен, а return - это результат успешности выполнения функции (если функция однозначна можно было бы использовать void, но по привычке все используют int).
То что в итоге нагородили в C++ - это безумие, которое не только интуитивно не понимают даже "как бы" продвинутые разработчики, но и легко создать трудноуловимые ошибки, при этом ещё и количество строк кода резко увеличивается решая исключительно формальную обслуживающую задачу, а единственный профит от этого - это то, что класс потом можно будет использовать эффективно, однако если потребуется что-то поменять то в C-стиле можно переписать несколько строк и всё без труда заработает, а в C++ rvalue-ссылками придётся следить чтобы нигде ничего не сломать. +Ещё выбрали обозначение идиотское (&&) и тоже не интуитивное (по логике это как взятие адреса у адреса), обычные ссылки имеют логическую подоплёку, т.к. & символизирует "адресность", что символизирует в этом контексте && - чёрт его знает, просто "потому что потому". В результате получается так, что даже в C++ чаще можно встретить работу без копирования на обычных указателях в стиле C вместо танцев на граблях с move-семантикой в стиле Rust. Кстати почти никто из "знатоков" move семантики не рассказывает что именно происходит с памятью, большинство я уверена даже сами не знают :), и вообще move-семантику если и изучать/преподавать, то лучше отвлечься от кода и нарисовать области памяти и на таком примере пояснить что именно мы делаем, так заодно сразу же всплывёт разница между lvalue и rvalue ссылками, а видео, в целом, неплохое, лучше чем у многих.
Ввели rvalue только с одной целью - продать больше книжек по C++ новой редакции. Просто бизнес. 🤣
В общем, поковырявшись самостоятельно, выяснил, что не так всё это работает, как тут рассказано. Перегрузка оператора присваивания для перемещения будет работать только если уже существующему объекту класса будут присваиваться другие значения. То есть, не при первоначальном создании объекта, как тут показано, а при изменении существующего объекта посредством оператора присваивания. Вот блин, ну и "препод", ё-мое. Походу, он сам толком не знает, как конкретно это работает. Так-с, щас надо разобраться с тем, что понаписано в самих реализациях конструкторов и операторов, там чето тоже лажа какая-то стопудово чето. Кароч, втопку таких горе-преподов, сжигать на костре инквизиции их надобно...
Кароч, детально разобравшись в вопросе, подытожу - автор видоса допустил принципиальную ошибку в использовании перемещающего оператора и вообще в понимании всего процесса работы. Перемещающий оператор присваивания не будет вызываться при создании нового объекта класса Array a = Create(); Он будет вызываться только для УЖЕ СОЗДАННОГО объекта. Ну и в показанном примере все что написано, в принципе не нужно, работает и без него без каких-либо проблем. Прискорбно, что сей горе-препод из Бауманки пытается учить других, при этом сам не понимает, как тут реально работает. Ему всего лишь нужно было запустить пошаговую отладку и проследить за исполнением кода. Он наверно потому и не стал запускать отладку, что она покажет совсем не то, про что рассказывает препод. Печально конечно. Сей бодячий фейл показывает истинное состояние дел в нашерашке в сфере ИТ, в противовес тому, что сыплется на нас с зомбоящика.
такое ощущение, что этот маленький ребёнок в универе преподом работает. это не лечится
вы не умеете объяснять, преподавание - не ваше
C++ не ваше, учите питон дядька
@@sashatim8244 тогда сразу же нашел отличное объяснение, но естественно, как всегда, в англоязычном сегменте. А что мне учить без вас разберусь. А проблема отечественных «учителей» она тянется еще с незапамятных времен. Любые книги, учебники, видеоуроки убоги за очень редким исключением. Это касается не только ЯП, а вообще всего.