You Gotta Frontend. Використай YGLFLovesDOU та спілкуйся із провідними спікерами зi всього світу!
×Закрыть

Изучение C++ для Java-разработчика

Давно уже думаю над тем, чтобы расширить сругозор и изучить C++. Сам я Android-разработчким с 5-летним опытом, и мой основной язык — это Java. Потому вопрос интересует именно в разрезе «добавить к знанию Java знание C++»: книги, сайты и т. п.

В Android-разработке иногда нужно часть приложения писать на C++ с целью улучшения производительности или разработки кроссплатформенного ядра, которое будет также использоваться на iOS: потребность очень даже практическая и осознанная — это нужно мне для дальшейшего развития и повышения ЗП.

LinkedIn

Лучшие комментарии пропустить

“Tsk, tsk,” said the Hatter, “what a mess you’ve made.”

“It is perfectly fine,” replied Alice calmly. “I will leave it for the garbage collection service to recover.”

“Don’t expect any garbage collection here. Furthermore, your polymorphic variables won’t ever be properly deleted, because you haven’t declared your destructor to be virtual.”

“My what to be what?” said Alice, starting to get worried.

“Declare your destructor. You must have a destructor. Everything that is constructed should be destroyed; it’s only natural. Furthermore, if you are ever not quite what you seem, you should declare yourself to be virtual.”

“A rule to remember!” roared the Red Queen. “Never make a mess without cleaning it up first.”

“You can ignore her,” whispered the Dormouse, picking up the tea cake Alice had just set aside, “but you shouldn’t cast away const so lightly.”

Alice began to feel that this new world she found herself in was not quite the same as the cozy sitting room she had just left.

Timothy Budd (1999) C++ for Java Programmers. p. v

Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
знание C++

ИМХО такого «знания» нет. Вернее это слишком много для одного человека. Как и в случае с Java. JEE и Android это в принципе разные отрасли разработки, хоть язык программирования один и тот-же. С С++ все тоже самое, есть язык программирования — а есть области его применения. Можно: писать драйверы устройств и операционные системы, можно программировать графику компьютерных игр, можно мобильные приложения и т.д. Общего будет разве что язык программирования , все остальное будет очень специфичным и требовать громадный объем специальных знаний начиная с компиляторов их опций и заканчивая особенностями бизнес домена.

Книги по языку:

Bjarne Stroustrup, The C++ Programming Language, 4th Edition ISBN-13: 978-0321563842
Stephen Prata, C++ Primer Plus ISBN-13: 978-0321776402
Scott Meyers , Effective Modern C++ ISBN-13: 978-1491903995

По компилятору GCC (основной для Linux/Andriod)
Brian J. Gough и Richard M. Stallman ( An Introduction to GCC: For the GNU Compilers GCC and G++ ISBN-13: 978-0954161798

По Andriod

Сайты:
Andriod NDK:
developer.android.com/ndk/index.html
habrahabr.ru/post/203014
developer.android.com/ndk/guides/index.html

ТС, і как прогрес з хрестами? овладєл?

9 января, 11:46

С++ за 21 день, уже срок на подході...
ілі «джавісти .... ані ... тупийє»

хорошо что я на котлине пишу, а не на жабе, так бы обиделся

не важно, С++ для настоящых твердояїх поцанофф,
кто ніасіліл православні хрести, в рай с юними дєвствєнніцами не попаде (а в адъ с бородатими одмінами)

а кто хаскель освоил, куда?

на Олімп к вєчноживим 80 левела

Троллинга ради посоветую учить не кресты, где кроме указателей и мотни с памятью ничего принципиально нового по сравнению с жабой нету, а скалу с монадами и типами высших порядков!

Он же хочет писать что-то производительнее чем можно на JVM, а на Scala получится только массивно-параллельные задачи решать легче лучше чем на Java

на Scala получится только массивно-параллельные задачи решать легче лучше чем на Java

Массивно-параллельные задачи лучше решать на Go, это уже разжёвано 1000 раз.

ну зачем так троллить неокрепшие умы?

Кто троллит, где вы видели троллинг? вот пруф: dou.ua/forums/topic/16012

Да и Scaloid — тот ещё монстр

Да и Scaloid — тот ещё монстр

Интересно, ктото в реале подобными вещами пользуеться ?

Маргиналы, не слышавшие про Котлин

Котлин может быть заменой C++? Я что-то упустил из жизни?

Котлин умеет биндить С код и компилироваться в нейтив посредством LLVM. Полагаю, речь именно об этом.

Apple не принимает в App Store приложений с виртуальной машиной внутри. Тот же Xamarin компилится в нейтив для iOS по этой причине, хотя под Android просто пакует свою виртуальную машину. Так что для моих целей пока что не поможет :(

Ну, к слову, kotlin-native без виртуальной машины работает. Вот из документации выдержка:
Kotlin/Native is a technology for compiling Kotlin to native binaries that run without any VM. It comprises a LLVM-based backend for the Kotlin compiler and a native implementation of the Kotlin runtime library. Kotlin/Native is primarily designed to allow compilation for platforms where virtual machines are not desirable or possible (such as iOS, embedded targets), or where a developer needs to produce a reasonably-sized self-contained program that does not require an additional runtime.

Kotlin/Native currently supports the following platforms:
Windows (x86_64 only at the moment)
Linux (x86_64, arm32, MIPS, MIPS little endian)
MacOS (x86_64)
iOS (arm64 only)
Android (arm32 and arm64)
WebAssembly (wasm32 only)

Хорошо, а можно к нему забиндидься из обычного Java-кода на Android и из Swift-a на iOS?

Я лишь интересующийся, на котлине не пишу, потому не могу ответить экспертно :) Но насколько я понимаю, мешать Kotlin (который под JVM) с Java можно без проблем. А для нейтива, скорее всего, понадобится какая-то C прослойка. Что-то вроде Swift -> C -> Kotlin, Java -> JNI + C -> Kotlin.

Гляньте еще на Haxe, я его использую для нативной разработки под телефоны, например. Он транслируется в С++, Java и другие языки. Под андроид можно собрать как в виде Java, так и нативно аки С++. Под iOS только С++ (с возможностью биндить Objective-C, если надо).

PS. Я бы не советовал отказываться от идеи подучить С++.

То что Java и Kotlin можно мешать — это одно. Тут вопрос сбилдить Kotlin Native а потом к небу забиндиться из Java, что уже совсем другое.

Haxe

Первый раз слышу, никогда нигде не видел. Не думаю, что его знание будет релевантным умением.

Можно, но мультиплатформенность там на очень ранней стадии. Исходники на Kotlin можно использовать напрямую из Java на Android, а для iOS они могут компилироваться во Framework (github.com/...​master/samples/calculator) и использоваться из Swift/ObjC.

Ответ выше был адресован кейсу по использованию скалы.

Извините, не сразу врубился :)

Цитата из поста:

В Android-разработке иногда нужно часть приложения писать на C++ с целью улучшения производительности или разработки кроссплатформенного ядра, которое будет также использоваться на iOS: потребность очень даже практическая и осознанная — это нужно мне для дальшейшего развития и повышения ЗП.

Вот как сюда можно приткнуть Scala?

Вот как выйдет scala-native (Как только так и сразу), так сразу везде пыщь-пыщь настанет.

Спасибо, а сейчас что делать, пока «так сразу» не наступило еще? ))))))))

Спасибо, надо рассмотреть такую объективно полезную и актуальную возможность :D

скалу с монадами

go на Go

Ага, чую — скоро планшетный/мобильный «нэйтив», станет одним сплошным «плюсом». :)

Автору рекомендую сосредоточиться не на C++, а на «C с классами». Т.е. забить болт на новейшие «плюсовые» стандарты, осваивать старый-добрый ANSI C/С++ 98, как наиболее портабельный вариант в «эмбеде».
Пользовать классы, ограничить использование полиморфизма, ограничить использование шаблонов, не пользовать эксэпшены... Изучение синтаксиса начать — с... написания на C/C++ каких-нибудь программ. :)

Относительно STL (и прочих библиотек контейнеров) — рекомендую заглядывать «под капот». Чтобы потом не удивляться, почему программа с векторами или прочими стрингами работает жутко тормознуто как будто это Джава.

Для начала нужно основы выучить. По этому я уже нашел решение, а вот что дальше — уже много советов в теме, есть на что ориентироваться.

Какие-то у вас пещерные представления про C++.

«плюсы» вообще в эмбеде (и мобильной разработке, в т. ч.) лучше не пользовать — а пользовать С.
Но я добр и понимаю, что некоторые фичи «плюсов», всё-таки облегчают жизнь кодерку — при этом, не особо доставляя проблем с портируемостью.

Чушь. Современные плюсы (с нормальной современной реализацией STL) генерируют такой же код как и C. Только сложнее накосячить. «C с классами» — это несомненно ужас.

В «плюсах» накосячить очень легко — гораздо легче, чем в C.

А что касается СТЛ, «под капотом» я больше имел в виду, к примеру, то, как работает стандартный СТЛьный аллокатор. Тут неискyшённого кодерка поджидает куча граблей и непоняток. И таких вещей там много....

И что там не так с аллокатором?

И что может накосячить человек если у него есть std::unique_ptr и std::vector?

Много чего. Например, если решит в цикле наращивать vector по одному элементу. Почему нет? Bедь «сэйф» контейнер, и сам растёт — очень удобно... :)

Ну и растёт. Вставка — O(1). Realloc — O(n)

Нагрешит чел всяким-таким — и окажется, что программа шевелится ещё медленнее, чем жаба. А хотелось ведь «с целью улучшения производительности»..

П.С. Я даже не о вставке элементов, а об «аппенде» (push_back’ом)... Со вставкой — всё ещё хуже. :)

А я говорю что push_back с O(1) не сделает программу тормозной. В отличии от неграмотного использования realloc. А человек способный грамотно разобраться в malloc/free осилит и std::vector::reserve

А человек способный грамотно разобраться в malloc/free осилит и std::vector::reserve

Чтобы начать осиливать reserve — для начала, придётся осилить что делается в push_back). Т.е. лезть «под капот»...

П.С. Там делается не реаллок, а нью-копи-делит. ;) И отвечает за это дело стандартный аллокатор...

нью-копи-делит

std::move_if_noexcept

нью-move_if_noexcept-делит?

Невелика разница.

Хотя я видел реализации с копи (вызов конструктора копирования для отдельных элементов, кажись).

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

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

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

Так что, придётся ковырять самому — или натыкаться на вилы во многих местах.

Не знаю, о каких книгах идёт речь. Те, которые я читал (Саттер-Александреску, Майерс), как раз нормально всё объясняли.

И что не так с аллокатором?

Так что там не так с аллокатором?

Например, если решит в цикле наращивать vector по одному элементу.

Це не є C++ specific. В жабівському ArrayList рівно та сама фігня. Це про знання структур даних, а не про реалізацію STL.

В жабівському ArrayList рівно та сама фігня.

Это действительно вопрос про знания структур данных.
Но реализация джавовского ArrayList-а отличается от STL Vector. И наращивание по 1 елементу там не настолько болезненно. (Хотя тоже болезненно)

Це про знання структур даних, а не про реалізацію STL.

Структура данных «вектор» никак не определяет способ выделения памяти. Лишь возможность последовательного расположения элементов, да возможность прямого доступа по индексу.

«плюсовой» стандарт накладывает дополнительные требования непрерывности памяти под STL-ным «вектором» (хотя, вроде, не все реализации STL ему следуют).

А выделять память можно многими разными способами. Скажем, даже в STL-ном векторе есть reserve — благодаря которому, последующие push_back’и могут работать без выделений/копирований. Контейнеры Electronic Arts (EASTL) работают с плэйсмент-констракшн. Всякие кастомные контейнеры могут быть с реаллоками (что, впрочем, не гарантирует от нью-копи-делит).
Жаба работает тоже с плэйсмент-констракшн — т.к. при старте приложения отгрызает себе для этого кучу памяти.

В общем, учите матчасть, кодерки. А то, в соседней теме пишут, что кодеркам Украины полу-хардскилл (знание предметной области) не нужен — так вы ещё и в хардскилле дружно плаваете...

Оп-па, а кодерок не в курсі, що це за структура даних!) Записуй, ця структура даних називається «динамічний масив». Тепер дуй швиденько у уікіпедію і вчи, чому тобі тут люди пишуть про О(1), бо це — пов’язано з основним, що треба знати про способи виділення пам’яті тут. Повернеся — доповіси.

учи матчасть и не пустословь

Советую посмотреть реализации векторов, и понять, почему добавление нового елемента в вектор — не операция постоянного времени.
Кроме того узнать, почему вектор жрет в 1,5 раза больше памяти, чем простой массив.
И заодно узнать, как в редких ситуация можно пожертвовать памятью ускорить работу с вектором.

Советую прочитать про амортизированное константное время.

Кроме того узнать, почему вектор жрет в 1,5 раза больше памяти, чем простой массив.

У вектора есть только три дополнительных элемента: указатель на собственно массив, размер выделенного блок и размер задействованной области. Ну никак не 1.5 раз больше, как в ваших фантазиях.

Никто не узнает, почему вектор жрёт в 1,5 раза больше памяти, потому что об этом никто, кроме тебя, не напишет. Так что будь добр, объясни.
А то я по-прежнему верю в миф, будто вектор хранит два размера (size и capacity) и указатель на динамический кусок памяти, который занимает ровно sizeof(T)*capacity байтов.

почему добавление нового елемента в вектор — не операция постоянного времени.

amortized(!) constant time. Я не писав, що вставка — це О(1), бо це буде дещо спекулятивно, бо amortized. Але там таки навіть не логарифм, мені здається, що саме через нерозуміння, чому навіть не логарифм, виникли оці гнилі понти до випадка, що вкрай рідко має практичне значення.

вектор жрет в 1,5 раза больше памяти

Що це за 1.5? Growth factor? Якщо це він, то виникає питання, в якій реалізації. MSVC vs GCC він може легко бути різним, читайте хоч вікі, блін, якщо не «реализации векторов». Потім, це worst case. Далеко не завжди. Якщо заздалегідь відомий розмір — знайомий із динамічним масивом зробить ресайз під скільки треба і буде рівно (ну плюс службові поля). Якщо не відомий, то чому ви не пишете, що це worst case? Як просто «на скільки більше жере» — bullshit.

можно пожертвовать памятью ускорить работу с вектором.

Знов дуже загальне твердження. Доступ можна зробити швидше? Зізнайтесь, ви про те, що збільшення growth factor може саме процес зростання дещо пришвидшити. Ну але через константну в середньому у push_back і те, що для відомих розмірів і так ресайз робиться — це не має великого значення. Корисніше про emplace_back у Маєрса почитати, не лізучи в реалізації.

Всякие кастомные контейнеры могут быть с реаллоками (что, впрочем, не гарантирует от нью-копи-делит).

Эээ, а как реаллок работает то, лол? Или вы видите сильную разницу между malloc-memcpy-free и new-move_if_noexcept-delete ?

плэйсмент-констракшн

Что-что? Placement new?

Жаба работает тоже с плэйсмент-констракшн — т.к. при старте приложения отгрызает себе для этого кучу памяти.

«Слышал звон, да не знаю где он»

В общем, учите матчасть, кодерки.

Самокритично

Эээ, а как реаллок работает то, лол?

Учи матчасть, кодерок,. Перестанешь задавать глупые вопросы.

П.С. «Senior Expert C++» (украинский) — не знает, как работает реаллок, чем «плэйсмент-констракшн» отличается от «Placement new» и как работает с памятью жаба.
Умора. :)

Учи матчасть, кодерок,. Перестанешь задавать глупые вопросы.

Учите, кодерок, учите, а не разговаривайте с сами с собой на форуме.

чем «плэйсмент-констракшн» отличается от «Placement new»

Не томи! Чем они отличаются?

«Placement new» — это частный случай «плэйсмент-констракшна». :)

Я под «плейсмент констракшеном» понимаю вызов конструктора нового объекта на заранее выделенном участке памяти. То, чем занимается placement new.
«Частным случаем» его с какой-то стороны можно назвать, но разве он и не единственный по факту?

У стандартных аллокаторов есть метод construct, который делает то же самое. Под капотом он выражается через тот же placement new, что прямым текстом прописано в стандарте.
Его можно переопределить в кастомном аллокаторе, чтобы выполнять какие-то дополнительные действия (к примеру, логировать конструирование объектов) — но всё равно я не представляю, как здесь можно обойтись без placement new.

Я под «плейсмент констракшеном» понимаю вызов конструктора нового объекта

Это необязательно должен быть конструктор. Скажем, в С конструкторов нет — тем не менее, можно создавать объекты (к примеру, структуры с данными + указателями на фунцкии) на уже выделенной памяти, а можно под каждый инстанс выделять отдельно. При выделении отдельно (и большом количестве объектов) — будет более тормознуто, чем в жабе.

Сейчас я говорил про модель памяти C++. Там под созданием объекта подразумевается вызов его конструктора.
Ну, если мы не говорим о POD (которыми в частности являются и сишные структуры). Для POD вызовы конструкторов и деструкторов не требуются. Там мы сами инициализируем данные, как считаем нужным. Или не инициализируем никак :)
В случае таких структур любой современный плюсовый компилятор просто выбросит вызовы их конструкторов (в т.ч. placement new или allocator::construct) как действия, которые не делают ничего.

Там мы сами инициализируем данные, как считаем нужным.

До инициализации, нужно под них выделить память. А её можно выделять, по-разному — для каждого инстанcа отдельным эллокейтом (например, в цикле), либо для всех сразу одним большим куском.

Это уже отдельный вопрос.
Вектор идёт по второму пути:

для всех сразу одним большим куском.

Хотя в наших руках сделать так, чтобы память выделялась отдельно для каждого объекта, если уж мы по какой-то причине будем испытывать такую необходимость.
Для этого можно, например, заменить std::vector<T> на std::vector<std::unique_ptr<T>>.

Кодерок не знает термина memory pool?

Кодерок не знает термина memory pool?

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

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

Контейнеры Electronic Arts (EASTL) работают с плэйсмент-констракшн.

Как и std::vector. Он отдельно выделяет память, отдельно конструирует на ней элементы.

Как и std::vector.

Нет, не так. В EASTL содержимое вектора располагается на уже выделенной памяти — не только указатели на объекты, но и сами объекты. В этом разница.
В жабе, к слову, тоже.

A std::vector вызывает для инстанса копи-констрактор с дополнительным эллокэйтом (если ты пихаешь инстанс в вектор по значению).

Плюс к этому, перевыделения памяти (при помощи нью-копи-делит), если у std::vector’a нехватка...

располагается на уже выделенной памяти — не только указатели на объекты, но и сами объекты. В этом разница.

Не понял :( Можно подробнее?
Если std::vector хранит указатели на объекты, то будут храниться указатели. Если он хранит объекты по значению, будут храниться объекты.

Или ты имеешь в виду конструирование элементов непосредственно внутри объекта вектора в его внутреннем буфере?
Если так, то это типичная small size optimization и она в силу некоторых требований стандарта именно для std::vector’ов невозможна.
Но ведь не std::vector’ами едиными.
Например, существуют boost::container::static_vector (вообще не выделяет динамической памяти и конструирует элементы только внутри себя) и boost::container::small_vector (гибридный вариант: до N объектов располагает внутри себя, потом переходит на динамические аллокации).

A std::vector вызывает для инстанса копи-констрактор с дополнительным эллокэйтом (если ты пихаешь инстанс в вектор по значению)

Во-первых, если я пихаю по значению, то вызван будет копи или мув констрактор — в зависимости от того, передал я lvalue или rvalue.
Но это только при пихании по значению.
А ещё я могу сделать emplace_back, передав аргументы конструктора и сконструировав новый элемент вектора сразу на месте. Тогда никаких копи или мувов не будет.

Во-вторых, дополнительного аллокейта как правило не будет. Он будет только если size == capacity и вектору негде размещать новые элементы. Тогда ему придётся выделить больший кусок памяти и переместить старые данные туда.

Плюс к этому, перевыделения памяти (при помощи нью-копи-делит), если у std::vector’a нехватка...

Ну дык от этого никуда не деться. Только обычно это new-move-delete (throwing мув конструкторы — очень большая редкость), мувы относительно дешёвые.
Да, для некоторых типов элементов (т.н. «POD») можно было бы вместо этого всего обойтись сишным реаллоком (а выделять/освобождать память через malloc/free). Уверен, есть реализации таких кастомных векторов.
std::vector это ведь не идеал, оптимально работающий со всеми возможными случаями, — это такой себе reasonable default, который должен устроить юзера в большинстве ситуаций.

Но это только при пихании по значению.

Это типичная ошибка начинающих — особенно из тех, кто приходит в «плюсы» с менеджед-языков. Делают штуки, типа таких: vector< string> — а потом удивляются, почему всё ползает намного медленнее, чем в жабе.

Тут не только будет вызываться копи-констрактор для каждого запихиваемого push_back’ом стринга, но ещё и время от времени делаться «нью-копи-делит» всего vector’a (мувы от копи по скорости, практически не отличаются).

В общем, без «подкапотных знаний» — сплошные вилы и грабли. О чём я и завёл речь.

Это типичная ошибка начинающих

Я не начинающий, плюсы знаю достаточно хорошо.

Делают штуки, типа таких: vector — а потом удивляются, почему всё ползает намного медленнее, чем в жабе.

Выглядит довольно голословно.

Тут не только будет вызываться копи-констрактор для каждого запихиваемого push_back’ом стринга

Если string передаётся как lvalue, тогда да (а как иначе-то?).
Если string темповый (полученный по значению из вызова какой-то функции), только что сконструированный в месте вызова, или к которому явно применили std::move — вызовется мув-конструктор.

время от времени делаться «нью-копи-делит» всего vector’a

Нью-мув-делит.

(мувы от копи по скорости, практически не отличаются)

Иногда очень даже отличаются. Всё зависит от типа элемента.
Если там какие-то POD структуры, то конечно не будет отличаться.
Если std::string’и, то будет при условии, что их длина больше какого-то там количества байтов (после которого стринг начинает выделять динамическую память); в противном случае стринг будет использовать small size optimization и разницы действительно никакой.

А если элементами вектора являются, к примеру, другие векторы (или ещё более сложные объекты, содержащие внутри себя векторы в качестве полей) — разница будет огромной.

Если string передаётся как lvalue, тогда да (а как иначе-то?).

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

А small size optimization заканчивается на чём-то типа длины 10 байт. В общем, не особо влияет.

P.S. Автор темы хочет мигрировать в «плюсы» с жабы. Он будет начинающим — и без заглядывания «под капот» будет много где ходить по граблям. «Плюсы» довольно граблистый язык (в сравнении с C). :)

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

И опять двойка

Он в обоих случаях будет placement new. Только в одном будет вызван конструктор копирования, а в другом — мув конструктор

Иначе, кастомным аллокатором, который вызывает конструктор — но он оказывается «плэйсмент»

Не вижу разницы. Это будет плейсмент вызов копи конструктора. То же самое, о чём я и говорил.

«Плюсы» довольно граблистый язык (в сравнении с C). :)

Два сапога пара. У Си меньше заковыристых фич — и поэтому как бы кажется, что меньше возможностей наступить на грабли. Но на самом деле концентрация граблей на метр квадратный при кодинге на Сях не меньше. Просто эти грабли более однотипные. В отличие от плюсов, где можно найти грабли самых разных форм, размеров и расцветок :)

А если элементами вектора являются, к примеру, другие векторы (или ещё более сложные объекты, содержащие внутри себя векторы в качестве полей) — разница будет огромной.

Не будет. На базовом уровне, это будет разница между memmove() и memcpy(). В общем, околонулевая.

А указатели внутри объектов тоже memcpy менять должен?

А указатели внутри объектов тоже memcpy менять должен?

Ты не выучил матчасть. :)

Будет.
Копирование вектора — это выделение новой памяти и копи-конструирование на ней копий всех элементов оригинального вектора.
Перемещение вектора — это просто присвоение трёх полей (size, capacity, data pointer) новому вектору и зануление их у старого. Никаких аллокаций, никакого конструирования (даже мув-конструирования) элементов.

Перемещение вектора — это просто присвоение трёх полей (size, capacity, data pointer) новому вектору и зануление их у старого. Никаких аллокаций, никакого конструирования (даже мув-конструирования) элементов.

Ты серьёзно? Или это была шутка такая? :)

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

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

Ок, я разжёванно напишу: :)

Есть вектор — у него сайз, кэпэсити и указатель на область памяти с данными. Далее, область памяти под указателем стала маловата — нужно довыделить. Реаллока нет (т.к. это «неплюсово» типа, да и шаблоны будут мешать).

По твоему концепту, «просто присваиваем три поля (size, capacity, data pointer) новому вектору и зануляем их у старого». Так? А каким магическим образом, данные с области памяти под старым указателем — переместятся на область памяти под новым указателем, без memcpy/memmove()?
Старая область памяти (под старым указателем) будет прибита — т.к. она стала мала и нужна новая бОльшая область (под новым указателем).

А куда realloc делся?

Не в писывается в концепт. К тому же, он не гарантирует от маллок-копи-фри (хоть и делает вероятность не 100%-й).

При чём тут

область памяти под указателем стала маловата — нужно довыделить

к перемещению (move-конструированию или move-присваиванию) вектора?
Ты не о том говоришь.

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

Их не надо никуда перемещать. Мы просто скопировали тот же указатель в новое место, а в старом (во избежание double delete) занулили. Всё.

к перемещению (move-конструированию или move-присваиванию) вектора?
Ты не о том говоришь.

Ну нельзя же кодерку иметь фокус и память улитки. :)

Я-то как раз о том-самом говорю. Обсуждаю, все те же проблемы граблей в «плюсах» для новичков, на примере push_back в векторе — при котором 1) время от времени, происходит перевыделение памяти, типа «нью-копи-делит» 2) или «нью-мув-делит» — по быстродействию от «нью-копи-делит», практически, не отличающийся.

Ты рассказываешь, что с мувом («нью-мув-делит») — всё будет работатъ гораздо быстрее, т.к. «„просто присваиваем три поля (size, capacity, data pointer) новому вектору и зануляем их у старого“».

Соответственно, мой вопрос тебе: «каким магическим образом, данные с области памяти под старым указателем — переместятся на область памяти под новым указателем, без memcpy/memmove()?»

Вообще не читаешь, что тебе пишут. Или специально «троллишь». В любом случае ползи отсюда, улиточка.
Если ты серьёзно недопонял, то можешь перечитать внимательнее предыдущие сообщения. Сейчас речь шла о перемещении вектора, а не о реаллокации его внутренностей во время увеличения capacity.

Сейчас речь шла о перемещении вектора, а не о реаллокации его внутренностей во время увеличения capacity.

Я уж не знаю, о чём ты пишешь — но обсуждаемая тема, описана выше мною. Собственно, мною и была начата...

std::vector<int> x(100500);
std::vector<int> y(std::move(x)); // перемещение вектора x в y

Я о действиях, которые выполняются во второй строчке. Это просто присвоение трёх целочисленных полей и зануление их у оригинала.

Эти же действия будут выполняться для вложенных векторов при реаллокации чего-то вроде vector<vector<int>> или vector<Foo>, где struct Foo { ...; std::vector<Bar> _bars; ...; };

Я о действиях, которые выполняются во второй строчке. Это просто присвоение трёх целочисленных полей и зануление их у оригинала.

И как это относится к «нью-мyв-делит», при push_bask’e? Тоже так мyвится? :)

Да, при реаллокации вектора векторов или вектора составных объектов, полями которых являются векторы (либо другие динамические контейнеры), вложенные контейнеры именно так и мувятся во время «нью-мув-делит» при push_back’е.

вложенные контейнеры именно так и мувятся во время «нью-мув-делит» при push_back’е.

Не, кодерок, тебе рано пользоватъся плюсами. :)

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

Не кому-то с твоим уровнем понимания судить, рано мне пользоваться плюсами или нет.

Ты не понимаешь, о чём тебе говорят. Перечитай ещё раз. О вложенных векторах.
Представь себе vector<vector<int>> x;
Для внешнего вектора x выполнится то, о чём ты сказал. С этим же никто и не спорит.
А для каждого из внутренних векторов (x[0], x[1], ..., x[x.size()-1]) выполнится то, о чём сказал я.
И здесь нет никаких противоречий.

А для каждого из внутренних векторов (x[0], x[1], ..., x[x.size()-1]) выполнится то, о чём сказал я.

Если «внутренние» вектора не будут смувлены (т.е. физически переписаны в другую область памяти) — они будут прибиты.

Вот об этом я и говорю. О том, как они будут смувлены. Для них не будет выделяться новая память.

Для каждого из них просто создастся новый объект типа vector<int>, внутренний указатель для которого перепишется из старого вектора, а в старом занулится.
И старый после этого как бы будет уничтожен, но это уничтожение уже ничего не сделает, т.к. указатель нулевой и освобождать нечего — всё уже было передано новому владельцу.

Их 12 байт (или 24 байта на 64-битной платформе) будут физически скопированы в другую область памяти, а в старой занулены, да. Это и есть перемещение вектора с точки зрения C++11.

И оно выполняется всяко быстрее копирования вектора, которое пришлось бы делать в аналогичной ситуации в C++98/03. Потому что копирование вектора подразумевает новую аллокацию и копи-конструирование в ней каждого элемента (которое в свою очередь тоже может быть недешёвым).

Их 12 байт (или 24 байта на 64-битной платформе) будут физически скопированы в другую область памяти, а в старой занулены, да. Это и есть перемещение вектора с точки зрения C++11.

И оно выполняется всяко быстрее копирования вектора, которое пришлось бы делать в аналогичной ситуации в C++98/03. Потому что копирование вектора подразумевает новую аллокацию и копи-конструирование в ней каждого элемента (которое в свою очередь тоже может быть недешёвым).

А, пардон, это же СТЛ — никакого меммува области не будет. Будет поэлементный мув. Просто, раньше для каждого элемента вызывался конструктор копирования, а сейчас вызывается некий «конструктор мува».

По скорости, думаю, один хрен. Может быть мув даже дольше — т.к. копирование не предусматривает обнуления инстанца, с которого копировали.

В стандартной реализации реаллока с побитовым копированием старого содержимого — да, меммува/мемкопи всей области целиком не будет, поскольку это плохо сочетается с конструкторами/деструкторами, которые по умолчанию подразумеваются (хоть и у некоторых типов ничего не делают).

Но на основе STL много контор пилят свои похожие библиотеки, где для некоторых типов данных (у которых конструкторы и деструкторы пустые — например, POD-типы) такой подход вполне можно реализовать.
Видел такое в геймдеве.

А стандартная библиотека не преследует цели быть оптимальной во всех возможных случаях. Она — такой себе reasonable default, который подойдёт для большинства задач. Но абсолютный максимум возможного перформанса из неё редко когда выжмешь.

Может быть мув даже дольше — т.к. копирование не предусматривает обнуления инстанца, с которого копировали.

Ну это вряд ли, если альтернатива этому обнулению — новая динамическая аллокация и цикл по всем элементам, которых может быть больше сотни.

если альтернатива этому обнулению — новая динамическая аллокация и цикл по всем элементам, которых может быть больше сотни

Дык, то же самое делается и при муве. :)
Т.к. всё равно нужно выделять память, под то — куда мувнуть. И в условиях СТЛ, это выделение будет тоже поэлементным.

Блин, сколько можно ходить по кругу? Мне это уже надоедает :(
Для sizeof(ElementType) * new_capacity байтов внешнего вектора — да, выполнится новая аллокация.
А элементы этого внешнего вектора (чем бы они ни были) из старого участка памяти в новый будут перемещены (используя мув-конструктор), а не скопированы, как в старых стандартах.
Я говорил вот об этом.

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

Ок, подебажил в VC++ 2010 — там плэйсмент-нью вызывается, для каждого элемента. Вида:
void _FARQ *_Vptr = _Ptr;
::new (_Vptr) _Ty1(_STD forward<_Ty2>(_Val));

Так что, возможно с копированием даже быстрее. Т.к. опять-таки, при копировании затирать источника не надо.

Про это я уже сказал.

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

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

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

Дык, не будет новых аллокаций. Область памяти под содержимое выделяется одним куском. А далее, для каждого элемента вызывается плэйсмент-нью на уже выделенном куске (неважно, плэйсмент-нью копирования или мува).

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

Дык, не будет новых аллокаций

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

для каждого элемента вызывается плэйсмент-нью на уже выделенном куске (неважно, плэйсмент-нью копирования или мува)

Важно.

Потому что этот плейсмент нью копирования может сам внутри себя выполнять динамические аллокации и пускать долгие циклы по большому количеству элементов. Как, собственно, и происходило бы в случае vector<vector<int>> для внутренних векторов, если б не мув-семантики.

А плейсмент нью мува вектора выполнит чуть больше присваиваний, но обойдётся без динамических аллокаций и циклов по элементам — пусть их там будет хоть миллиард.

Не представляю, каким должен быть аллокатор вектора, чтобы его копирование (разумеется, на небольшом количестве элементов) работало быстрее перемещения. Было бы интересно такое увидеть :)

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

Вот поэтому оверхед от мува и будет ощутимо меньше, чем оверхед от копирования.

Если же копирование некоторого специфичного класса выполняется быстрее, чем выполнялся бы его гипотетический «мув» с занулением, — значит его авторы скорее всего просто не захотят реализовывать такой мув. Оставят только копирование, по старинке.

Потому что этот плейсмент нью копирования может сам внутри себя выполнять динамические аллокации и пускать долгие циклы по большому количеству элементов.

Не, так конструкторы копирования, с полями-указателями на динамические данные — никто не пишет. Пользуют всё те же шэред-поинтерс, прочие аттачи/детачи, итп.

Дефолтные (автогенерированные) конструкторы кoпирования — тоже «дип-копи» не делают.

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

P.S. Но для контейнеров, в принципе, «мув-семантика» имеет смысл. T.к. конструктор копирования c «дип-копи» (если такой выложат) — будет явным и бесполезным тормозом.

Не, так конструкторы копирования, с полями-указателями на динамические данные — никто не пишет.

Вся стандартная библиотека контейнеров их имеет.

P.S. Но для контейнеров, в принципе, «мув-семантика» имеет смысл. T.к. конструктор копирования c «дип-копи» (если такой выложат) — будет явным и бесполезным тормозом.

Вот я ж о том же.
И он не совсем бесполезный. Иногда мы действительно хотим сделать глубокую копию контейнера. Но очень редко — явно реже, чем за неимением мув-семантик такие конструкторы вызывались до C++11.

Пользуют всё те же шэред-поинтерс

Кстати, о птичках. Для шаред поинтеров мув-семантика тоже имеет большой смысл. Потому что копирование шаред_поинтера в новое место (если в старом он больше не будет нужен) — это лишние атомик операции, которые далеко не дешёвые.
Сначала надо при создании копии шаред_поинтера атомарно заинкрементить счётчик ссылок, а потом (при уничтожении старого экземпляра) этот же счётчик ссылок атомарно задекрементить. Дурная работа.
С мувом же никаких атомик операций вообще не происходит. Всё так же переписали указатель в новое место и занулили его в старом, счётчик ссылок оставляя при этом нетронутым.

А ещё мув-семантика незаменима для тех типов, которые в принципе невозможно копировать. Самый простой пример это unique_ptr.

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

Двойного удаления не должно быть, т.к. при реаллокэйте — деструкторы элементов на старом куске не вызываются. Просто, весь старый кусок убивается, со всем содержимым.

Вызываются.

Для кого раньше вызвался конструктор (неважно, какой именно: хоть дефолтный, хоть со специфическими параметрами, хоть перемещения, хоть копирования) — для того должен вызваться и деструктор.
Соотношение один к одному.

Так что если элементы из старой области памяти мы скопировали (с помощью копи-конструктора) или переместили (с помощью мув-конструктора) в новую область — для элементов в старой области перед её деаллокацией должны вызваться деструкторы.

Вспомни, как было до C++11, когда при переезде вектора все его элементы только копировались.
Представь себе тот же vector<vector<int>> v;.
Для его элемента v[0] (имеющего тип vector<int>) вызвали конструктор копирования — значит, аллоцировали новую память и скопировали все int’ы туда.
Старый кусок памяти необходимо освободить.
Если бы после этого деструктор для старого вектора v[0] не вызывался — происходила бы утечка.

Для кого раньше вызвался конструктор (неважно, какой именно: хоть дефолтный, хоть со специфическими параметрами, хоть перемещения, хоть копирования) — для того должен вызваться и деструктор.
Соотношение один к одному.

Не, деструкторы нельзя вызывать — т.к. они грохнут память, на которую ссылаются копируемые/перемещаемые элементы.

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

Можно и нужно. Так всегда было и будет.

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

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

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

Копируемые элементы необходимо грохать, раз они стали ненужны.

Блин... Проверил — в натуре деструкторы вызываются. Но это же кошмар...

P.S. Давно не пользовал я в «плюсах» стл. :)

Не кошмар. Иначе были бы утечки при реаллокации вектора типа vector<vector<int>>, к примеру.
В случае с копированием потребность вызывать деструктор у источника лежит на поверхности.

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

Но здесь проблема как минимум в том, что после того, как объект мувнули, никто больше не помнит о том факте, что его именно мувнули. Чтобы для этого особого случая каким-то образом «отключить» деструктор.

«Запоминать» специально никто такие вещи не хочет — это были бы лишние накладные расходы. Да и где бы такую инфу можно было запоминать — отдельный вопрос, не имеющий однозначного ответа.
Проще уж при необходимости занулить ему указатели, если без этого его деструктор потёр бы что-то чужое.

Представь себе тот же vector> v;.
Для его элемента v[0] (имеющего тип vector) вызвали конструктор копирования — значит, аллоцировали новую память и скопировали все int’ы туда.

Перемещается лишь инстанс «внутреннего» вектора — это сайз, кэпэсити и указатель.
Память, на которую указывает «внутренний» вектор (массив int) никуда не копируется и вообще не трогается. Потому и деструкторы при реаллоке («нью-мув-делит) не вызываются.

Если скопировать/двинуть и массив int — то нужно менять указатель и «мув-семантика» не будет работать.
И «копи-семантика» не будет работать тоже (без «дип-копи»)

Ладно, ты меня не понял, но просто поверь что я прав а ты нет :)
Я уже подустал от этих попыток кому-то что-то объяснять в интернете. Мне здесь за индивидуальные уроки не платят.
Погугли на досуге, почитай cppreference, подебажься. В крайнем случае ради эксперимента попробуй навелосипедить свой вектор и реализовать для него те операции, о которых мы говорили — а потом сделать композицию этих векторов (а ля vector<vector<int>>), оценить подводные камни. Всё увидишь сам.

В крайнем случае ради эксперимента попробуй навелосипедить свой вектор и реализовать для него те операции, о которых мы говорили — а потом сделать композицию этих векторов (а ля vector>), оценить подводные камни.

Я самописные контейнеры пользую в «плюсах» уже последние лет 10. :)

Туда ничего в принципе нельзя запихнуть по значению, кроме самых примитивных типов. И деструкторы при резервировании не вызываются.

Прадва, резервирование возможно лишь в бОльшую сторону — «шринка» нет, а там был бы смысл вызывать деструкторы.

И оно выполняется всяко быстрее копирования вектора, которое пришлось бы делать в аналогичной ситуации в C++98/03. Потому что копирование вектора подразумевает новую аллокацию и копи-конструирование в ней каждого элемента (которое в свою очередь тоже может быть недешёвым).

Дык, будет точно такое же поэлементное аллоцирование (потому это и называется «констрyктор»).

Иначе, без аллоцирования, откуда возьмётся вот эта «другая область памяти» — («будут физически скопированы в другую область памяти») ? :)

Аллоцирование другой области памяти для внешнего вектора конечно будет.

А процитированный кусок моего комментария относился уже ко внутренним объектам, которые дорого копи-конструировать, но дёшево мув-конструировать.

Блин, я уже не помню — надо будет подебажить и посмотреть. :)Вроде, там область памяти под данные выделялась-таки одним нью (типа нью Т [новый кэпэсити]), но и конструктор копирования вызывался для каждого элемента.
В общем, надо глянуть...

Да, так и было.

Сейчас память всё так же выделяется одним куском (allocate(sizeof(ElementType) * new_capacity), где allocate(size_t size) метод аллокатора, по умолчанию аналогичный вызову ::operator new(size) — который, в свою очередь, аналог сишного malloc’а).

А потом уже для каждого элемента вызывается конструктор... только как правило перемещения, а не копирования.

Точнее, move_if_noexcept. Если конструктор перемещения гарантирует, что он никогда не швырнёт эксепшн, то будут вызываться конструкторы перемещения. Иначе — по старинке конструктор копирования (ради exception safety: чтоб если посреди копи-конструирования элементов какой-то конструктор таки решит чем-то швырнуться — у нас хотя бы остался старый вектор в рабочем состоянии).

вложенные контейнеры именно так и мувятся во время «нью-мув-делит» при push_back’е.

Я тебе приведу пример на пальцах:

Ты выше написал: vector < Foo > . Пускай Foo будет структурой, размером 10 байт. Для простоты забьём на всякие алайнменты итп приколы компилятора — и посчитаем, сколько памяти будет под вектором из 10 таких структур. Это будет 100 байт.

Далее, сделали push_back ещё 1 структуры, во время которой пришлось реаллоцировать. При этом делается «нъю» области в 10 * 1.6 (думаю, что-то типа этого, в большинстве реализаций), т.е. 160 байт новый размер.

Что при этом делается с 100 байт, которые уже заполнены 10-ю структурами? Они никуда не мувятся (мемкопи или меммyвом)? Тогда они будут прибиты, последующим «делит» этой области.

Вообще не о том шла речь. Ты снова заговорил о реаллокации наружного вектора, а не о том, что происходит с его элементами (с каждым из внутренних vector<int> при реаллокации содержащего их vector<vector<int>>).

Ок,

vector < vector < int > >

.

Тут каждый элемент «внешнего» вектора — будем считать для простоты равным 12 байт. По 4 байта размер, ёмкость + указатель.

Если во «внешнем» векторе 10 «внутренних» векторов — это 120 байт. Если нужна реаллокация памяти под указателем внешнего вектора — эти 120 байт будут физически переписаны на новую область памяти. А не некой «мув-семантикой» адресованы непонятно, откуда...

Так с этим же я и не спорил. Будут.
Зато каждый из них мог владеть ещё 100500 байтами — и эти все данные сохранятся где были без всяких изменений благодаря мув-семантике. Об этом, собственно, я и говорил всё это время.

Зато каждый из них мог владеть ещё 100500 байтами — и эти все данные сохранятся где были без всяких изменений благодаря мув-семантике. Об этом, собственно, я и говорил всё это время.

Ну, как бы, да. Происходит обычный «меммув». С дополнительными пассами (в виде обнуления).

Но при этом, что эти 120 байт будут смувлены, что скопированы — различия в быстродействии практически не будет.

Я говорил не о том «муве».
Так-то да, 120 байт будут по факту скопированы в новое место, и от этого никуда не деться.
А мув-семантика из C++11 помогает дёшево переместить внутренние данные вложенных элементов в новые места.
Чтобы не выполнять новых аллокаций и глубоких копирований данных для, например, вложенных vector<int> при переезде vector<vector<int>> в новое место.
Вот, я об этом говорил.

Ты рассказываешь, что с мувом («нью-мув-делит») — всё будет работатъ гораздо быстрее, т.к. «„просто присваиваем три поля (size, capacity, data pointer) новому вектору и зануляем их у старого“».

це більше смахує на swap

мувы от копи по скорости, практически не отличаются

И опять вас нужно послать почитать матчасть

И опять вас нужно послать почитать матчасть

Я уже давно понял, что ты не в теме. Продолжать путаться под ногами у взрослых дядь, обсуждающих сложное — тебе незачем. :)

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

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

Ну еще не стоит лезть со своим невежеством на форум программистов, тут-то найдутся люди грамотнее вашего и поднимут вас на смех. А такое троллю не нужно!

не пустословь — подучи матчасть...

Це все дуже залежить від контексту і від задачі і від заліза.
Можливо, він справді буде нарощувати в одному місці вектор поелементно(і це може тормозити, скажімо, в 10 разів), а в наступному фрагменті коду, використання вектору покращить швидкодію в 20 разів або, що найбільш вірогідно, автор буде працювати на залізі, на якому взагалі не буде помітна різниця, бо, по-перше, теперішні проци швидкі, а пам"яті хоч жопою жуй, а, по-друге і що найбільш важливо, сучасні компілятори оптимізують код так, як ні один сіньйор не вміє. Тому важливо писати правильні алгоритми, а структури даних компілятор в 99,99% випадках оптимізує набагато краще вашого.
Тому автор, вчи алгоритми.

а почетверти, виявиться, що в системі є кілька рівнів кеша, і вся оптимізація з векторами летить до дупи....

Тому важливо писати правильні алгоритми, а структури даних компілятор в 99,99% випадках оптимізує набагато краще вашого.
Тому автор, вчи алгоритми.

... і виявиться, що тре робити профайлінг на тергет системі, а не гадати на алгоритмовій гущі

Не слышал про code bloat для С. В С++ с STL — классика.

Скажу от себя, хотя и не работаю ни в какой компании, сам по себе фрилансер.
Изучал и изучаю С++ и другие технологии, которые с ним связаны (STL, Qt) уже как 2 года.
Мой вам совет такой, начать с книги
Прата С. — Язык программирования С++. Лекции и упражнения — 2011

Очень доступно все и хорошо описано. Можно потом перейти к Б.Страуструп — Язык С++.
В данной книги автор делает упор больше на дизайн, как нужно писать на С++, так что если базиса в С++ нет, то кажется, что все очень сложно с С++.
После можете почитать Мейерса, Саттера, Александреску.
Для STL, рекомендую Стандартная библиотека С++ — Н.Джосаттис
Опишет всю стандартную либу + многопоточность( не в деталях, но в общем будет понятно что к чему)

Скури классический С Кэрнигана-Ричи. Когда поймешь, что такое указатель, препроцессор и как выделять память ручками можешь смело лезть в почти любой С++ код и читать его с листа — работу с объектами ты уже знаешь.

Особенно маты когда STL не скомпилилось

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

Я бы советовал начать со Страуструпа — Язык программирования С++ 3-е издание. Для того, чтобы стартануть этого достаточно. После этого сожно уже нюансы С++11 и выше начать постигать (Майерс и прочие).

Дико плюсую Майерса. Только на всякий случай уточню, что к C++11/14 имеет отношение только последняя его книга, «Effective Modern C++». Предыдущие имеет смысл читать после ознакомления с основами языка, чтобы избегать граблей и разобраться с хорошими практиками.
Сюда же совместная книга Саттера и Александреску «C++ Coding Standards».

Можешь попробовать книгу Страуструпа «A Tour of C++». Она очень короткая и быстро прогоняет читателя через основные аспекты языка. Но параллельно с чтением придётся много гуглить об упомянутых в книге вещах, т.к. они там особо не разжёвываются.
Данная книга скорее направлена на тех, кто когда-то писал на C++ и подзабыл, чтоб быстро освежить память и ознакомиться с основными нововведениями C++11/14. Но людям, изучающим плюсы после шарпа или джавы, её также можно рекомендовать.

Да, книга хорошая. Есть такая даже на русском.

мой основной язык — это Java. Потому вопрос интересует именно в разрезе «добавить к знанию Java знание C++»

Зачем??????

иногда нужно часть приложения писать на C++

раз на 100 проектів?

Зачем??????

Затем, что это даст возможность получать большую ЗП.

А еще C++ нужен не

раз на 100 проектів

а намного чаще.

Затем, что это даст возможность получать большую ЗП.

Намного больший прирост к зарплате будет от перехода с андроида в ентерпрайз.

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

С каких это пор экстремум в одной отрасли противопоставляются среднему значению в другой?

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

Одна людина vs не кожний. Thanks for proving the point!
У мобіл взагалі є обмеження, що на них не ганяють щось серйозне. І ніфіга ви з цим не поробите. Тому я б таки подумав щодо уходу з мобіл на майбутнє.

А может все таки посоветуете что-то по сути вопроса темы? Я все еще хочу выучить C++...

dou.ua/...​rums/topic/22744/#1259020
Я познав дзен гугла і тому питаю деінде настільки рідко, що це практично не можна помітити. І відповіді там скорішее кращі, ніж тут дадуть (і тут вам таки їх дали і непогані). Я не світчився з Java на C++, тому я теж буду гуглити, щоб порадити щось конкретне. Є момент, що трохи С++ /11 не завадить і цього не так багато може бути у світчерскій літературі. І мене теж можна питати, див. профіль, я з NDK працював, якщо шо.

Спасибо, тут не вопрос свича, а вопрос дополнить уменить писать на Java умением писать на C++.

Якщо ви не переключитесь — ви продовжите писати на Java!
Світчерська література — це саме ваш випадок, вчити С++ знаючи Java.

А що є серйозне? Хроміум ні?

Це не абсолютне твердження, але от взяти хроміум. Він є лише адаптацією під мобіли. Тобто не брали мобайл девів писати хроміум, брали — адаптувати. Такого багато під мобіли. І знову ж таки взяти хроміум — в нас нема гуглу як компанії що таке розробляє, до нас хіба шматки таких проектів будуть доходити. В Самсунгу у нас роблять до речі цікаві речі під мобіли, на кшалт OpenCL/OpenGL під залізо. Але знов — не візьмуть туди мобайл деву, там інше потрібно (і компенсація там теж ще та). Було б дуже цікаво інші приклади почути, може щось і дійсно є ;)

в нас нема гуглу як компанії що таке розробляє, до нас хіба шматки таких проектів будуть доходити

В нас був Яндекс, де як раз колупалися в кишках того Хроміума щоб Яндекс браузер на Андроїді був конкурентоспроможним.

Проект закрыли задолго до закрытия офиса, точнее тех ребят, которые адаптировали хромиум перевили на другие задачи. А хромиум дорабатывают в других офисах Яндекса.

Вообщем как всегда: любая тема «посоветуйте как изучить» превращается в «зачем оно тебе надо».

добре, спочатко вчимо С++ (який саме, з Qt, з бустом, якої версії, 03, 11, 14, 17?),
а потім думаєм, а напоркуя то все надо було?

а напоркуя то все надо було

Сначала я определился с тем, что оно мне таки надо, а потом пошел задавать вопрос, как его учить.

Мне нужно сначала выучить сам язык, его основу. Я не шарю в стандартах, но не Qt точно, так как это уже не основа.

А вот когда я смогу сказать, что научился хотя бы на базовом уровне кодить на самом C++, я уже посмотрю на какие-то фреймворки/библиотеки/надстройки и т. п., тем более, что они могут зависеть от конкретного проекта.

Так там учить джавісту на базовому уровні нема що, крім того, щоб навчитися маніпулювати із вказівниками і вперед за орденами.

Спасибо, а теперь скажите, пожалуйста, как выучить синтаксис C++ наилучшим образом :)

как выучить синтаксис C++ наилучшим образом :)

пиши С++ код, блеать!
macode.ru

Не-не, это не тот случай. Тут не в «посоветуйте как изучить» дело, а непосредственно в C++. Для flyman’а они как красная тряпка. Не обращай на него внимания, и он уйдёт.

Тут ти прав, С++, як красна тряпка, так як потім після джавістів, які тіпа виучили С++, приходиться розрібать їхні творчєства з натягування сови на глобус.

Как джавист, которому приходилось разгребать творчество С++ников, которые типа выучили java говорю: разгребать глобус, натянутый на сову тоже то еще удовольствие.

Ось тут дуже добрі книжки радять: stackoverflow.com/...​ook-guide-and-list#388282

їх там забагато

А ніхто не каже, що треба читати їх всі. Там написано про кожну, а людина сама обере що саме звідти взяти. Головне те, що там нема «гівняних» книжок.

От якраз цей топік для того, щоб порадили конкретні книжки. Нагуглити кожен може.

Там якраз радять конкретні книжки для різного рівня знань з описом що в книжці і для кого вона. І доречі С. Прату там не радять, бо є помилки в книжці.

Не знаю, як там у вас у джаві, але в дотнеті скоріше буде окремий розробник, який спеціалізується на плюсах. І потім вже його робота інтегрується в шарп код.

Воно нафік не впало. Краще зробити на нормальних плюсах, а далі або зробити сі-шну обгорку і потім через p/invoke, або COM/(lightweight COM). Це з досвіду доволі багатого.

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

Или от того, что у тебя есть C++ либа для iOS проекта на Swift-е...

Можно использовать objective c обёртку, и писать одновременно и на objective c, и на c++, стоит только иметь .mm расширение файла реализации вместо .m.

А тянуть objc код из свифта — намного легче.

А, что гугль не работает уже? По-моему информации в нете, о том, с чего начинать учить плюсы — море. Начните с книги C++ Primer Plus (6th Edition) Stephen Prata. Она есть в амазоне и на торрент-трекерах.

Гугл выдает тонны информации, которую просто невозможно оценить самостоятельно. К тому же я специально отметил, что буду изучать C++ как уже состоявшийся Java-девелопер, и вот на конкретно этот запрос гугл выдает чуть меньше, чем ноль. Выдает постоянно «Java после C++», а мне надо «C++ после Java».

Я вас понял. Тогда рекомендую начать с этой книги, можете пропускать материал если многое кажется знакомым, самые важные разделы, на которые вам стоит обратить внимание, это память (хранение объектов в памяти, какие классы памяти есть), автоматическое управление ресурсами (smart pointers и RAII), STL, ну а для тонкостей языка очень рекомендую книги Мейерса, но это уже после ознакомления с базой.

C++ for Java programmers/developers видає купу по темі. Це перше що я вбив.
UPD: java to c++ migration — теж більшість по темі, що ви шукали?

Тут надо не просто для джавистов, а для того, чтобы писать библиотечные функции под капотом у Андроида, которые можно дернуть из Джавы.

По опису таки мову хочуть. Тоді Android NDK — теж все гуглиться, є нормальні доки від гугла і певен що є купа туторіалів (і разом з NDK є sample apps). Що ще треба? І якщо просто деякі функції викликати, то проблем доволі мало, веселіше якщо native activity.

Що ще треба?

Знать C++ для начала :D

На C++ могут быть не только функции, на нем может быть написано все ядро программы, весь «внутренний бекенд», чтобы быть кроссплатформенным и не писать его дважды на Java и Swift под две платформы, а писать только UI. Тут надо знать не как цикл написать на C++, а знать язык целиком и уметь писать на нем с нуля.

Да, но надо учитывать, что писать будет значительно (в 2-3 раза) медленнее, чем на Джаве. И вероятно огрести кучу багов.

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

“Tsk, tsk,” said the Hatter, “what a mess you’ve made.”

“It is perfectly fine,” replied Alice calmly. “I will leave it for the garbage collection service to recover.”

“Don’t expect any garbage collection here. Furthermore, your polymorphic variables won’t ever be properly deleted, because you haven’t declared your destructor to be virtual.”

“My what to be what?” said Alice, starting to get worried.

“Declare your destructor. You must have a destructor. Everything that is constructed should be destroyed; it’s only natural. Furthermore, if you are ever not quite what you seem, you should declare yourself to be virtual.”

“A rule to remember!” roared the Red Queen. “Never make a mess without cleaning it up first.”

“You can ignore her,” whispered the Dormouse, picking up the tea cake Alice had just set aside, “but you shouldn’t cast away const so lightly.”

Alice began to feel that this new world she found herself in was not quite the same as the cozy sitting room she had just left.

Timothy Budd (1999) C++ for Java Programmers. p. v

по поводу кроссплатформенности: может стоит посмотреть на Qt?

Во-первых, в вакансиях я нахожу C++ довольно часто, а вот Qt я не видел ни разу. Это главная причина, почему мне нужно изучать именно C++, а не что либо другое.

Во-вторых, Qt — это фреймворк для C++. Не понимаю, зачем советовать Qt когда я еще не знаю C++...

ищите лучше...или не ищите, ваше дело
Qt упрощает порог вхождения в плюсы...но вы и так все знаете...

Если вы считаете, что изучить Qt можно до C++ и потом изучить C++, при этом еще и сделав изучение проще, то об этом стоило сразу написать прямо.

Для меня изучение библиотеки до изучения языка, на котором она описана, является очевидным нонсенсом. Если в данном конкретном случае это не так — агрументируйте вашу точку зрения.

ядро языка можно полистать 101.lv/learn/C /index.htm
более «жесткие» вопросы и извращения www.dietmar-kuehl.de/mirror/c -faq
для вдумчивого занудного чтения Страуструп (последнюю версию)
справочники en.cppreference.com/w www.cplusplus.com/reference
Язык иногда развивается, но для работы под капотом джавы должно хватить старого C++03
По оптимизациям www.agner.org/...​timize/optimizing_cpp.pdf

Тут и стоит вопрос чтобы изучить базу: синтаксис, особенности языка и главные различия с Java: ручное управление обектами и памятью (тут же нет GC, который все подметет) и прочее.

ядро языка можно полистать 101.lv/learn/C /index.htm

Выглядит неплохо, сойдет для моей начальной цели?

да и нет. сойдет для поверхностного знакомства с ядром языка — получить представление, что это. чего там нет:
1) стандартной библиотеки (STL (контейнеры и алгоритмы) и сишной библиотеки)
2) многопоточности (вообще не стандартизирована, желательно почитать про линуховый pthread — он более-менее базовый и он должен быть под Андроидом)
3) сложных штук и извращений, которые составляют половину плюсов
4) новых стандартов (C++11/17)
То есть, с этого можно начать, но потом обязательно почитать C++ FAQ Lite чтобы узнать, чего не знаешь; там и грабли описаны всякие. Ну и да, практика рулит после теории.
Можно пинать в скайп, если что-то непонятное попадается по ходу Skype: denys.poltorak

Спасибо. Начну с этого, надо ити постепенно. За скайп тоже спасибо, сохраню на тот случай, если будут сложные вопросы.

По STL ИМХО надо читать Страуструпа, но он совсем академический, близок к советским учебникам для ВУЗов. Но зато расскажет, как использовать алгоритмы и контейнеры. Но вовсе не гарантировано, что для андроида это надо — производительность у них не оптимальная.

Как я писл в шапке, тут вопрос не только производительности, но и кроссплатформенности, как бы странно это не звучало. Так как код на C++ можно скомпилировать и под Android, и под iOS, в отличие от Java и Swift, которые будут работать либо только на Android, либо только на iOS — без вариантов.

Сначала пройдусь по языку, а потом уже буду думать, что дальше делать.

Лучше учить Swift , он и красивее и перспективнее С++

Он только для Apple-платформ пока что, все остальное — разговоры. А C++ востребован здесь и сейчас и не ограничивается патформами Apple.

сравни даже циклы

Swift
for i in 0 ..< 8 {
}
если не нужен счетчик i можно вместо него _ написать, он даже инициализироваться не будет
и
C++
for( int i = 0; i < 8; i++ ) {
}

Мне не надо рекламировать Swift, в данный момент он мне не интересен от слова совсем. Тема про С++.

P.S. Перспективность языка определяется не красотой циклов, а практической возможностью его применения. ИМХО у Swift почти нет шансов выйти за пределы Apple-платформ, а Apple-платформы сами по себе меня не интересуют вообще.

Когда компилятор свифта перепишут с с++ на свифт — тогда поговорим .

Swift
for i in 0 ..< 8 {
}

Если учесть что они эту нотацию поменяли при смене версий то таки да впрочем узнаю барат аппле в вопросах обратной совместимости ))

если не нужен счетчик i можно вместо него _ написать, он даже инициализироваться не будет

Справедливости ради оптимизирующий компилятор «плюсов» всё «лишнее» прекрасно выбрасывает и «формально инициализированной переменной и» может не быть даже в случае если она таки как-то используется но «можно выбросить формально инстанс».

и
C++
for( int i = 0; i < 8; i++ ) {
}

Справедливости ради конкретно эта конструкция в «сях» сильно более интуитивно понятна в то время как свифте прямо сказать включает ли диапазон в себя 8 или наоборот нет без обращения к документации «ну просто знать надо» и опять же ж «на сях» достаточно записать:

for( int i = 0; i <= 8; i++ )

А вот на свифте придётся решить как правильно это пишется:

for i in 0 .. 8

или же ж:

for i in 0 ... 8

Что опять таки встретив такую запись «просто по коду» нужно знать конкретно «просто знать» включает ли диапазон в себя 8 или же ж нет (и соотв. логично предположить что для этого есть другой оператор а ну ок а какой?)

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

int v[10] = { 100 };
for ( int i : v ) {
...
}

Хотя да там в свою очередь начинается отдельная глубина потому как может быть и так:

int v[10] = { 100 };
for ( int& i : v ) {
...
}

Либо так ))

int v[10] = { 100 };
for ( int &i : v ) {
...
}

А то и вообще так:

int v[10] = { 100 };
for ( auto&& i : v ) {
...
}

Як на мене по STL Java програмісту можна читати спеки десь на en.cppreference.com/w/cpp/container та інше там. Стандартні контейнери не завадять в будь якому разі, великий шанс, що вони будуть хоча б поза time critical кодом, та й щось не вірю, що автору часто треба буде підбирати особливі контейнери під його задачі.

Насчёт Страуструпа в данном случае не соглашусь. Если человек уже состоявшийся разработчик, хорошо владеющий языком вроде джавы, для него это не будет стоить затраченных усилий.
Такому человеку я бы рекомендовал изучать стандартные контейнеры и алгоритмы по онлайн-справке на cppreference.com или cplusplus.com/reference.

Не согласен. Лучше таки прочитать Страуструпа, чтобы понять идеологию С++.
А потом Майерса, чтобы понять, как лучше и правильнее поступать в некоторых стандартных ситуациях.

Александреску забыли — чтобы понять, как можно, но не нужно делать

чтобы понять, как можно, но не нужно делать

Скорее, как делалось в некоторых шаблонных библиотеках 10 лет назад.
Сейчас эти идеи ещё актуальны, но их реализации очень сильно поменялись с приходом новых стандартов.
Кому интересна подобная наркомания, рекомендую заглянуть в Boost.Hana (Луи Дионн давал несколько выступлений на ютубе).

А писать такое самому не надо не из-за того, что это плохие практики, а тупо потому, что 99% необходимого уже давно написано и нефиг велосипедить без крайней необходимости.

Насчёт Майерса всеми руками и ногами за. Обязательный к прочтению материал для всех, кто хочет писать на плюсах, не напарываясь на грабли на каждом шагу.
А Страуструпа, конечно, не помешает, но я не считаю его таким уж обязательным. Идеологию C++ можно понять и по лекциям Саттера или того же Страуструпа на ютубе.
Хотя тут уже каждому своё. Кому-то удобнее почитать книжку, кому-то послушать выступления.

Вот я не читал ни Майерса, ни Страуструпа, а при разработке на C++, года с 98, ни на какие непреодолимые грабли не напарывался. Тем более на каждом шагу.

А я напарывался — на соляре при перегрузке глобального operator new() компилер падает. Пытался сделать циклический менеджер памяти (memory pool) для FCGI. Не вышло.

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

STL известен тем, что при доступе к контейнеру происходит 2 перехода по памяти: к объекту контейнера и к его хранилищу данных, которое отдельно выделено. В частности:
вектор против массива. чтобы вынуть элемент вектора, надо по адресу перейти к объекту вектора, вынуть из него указатель на массив, и потом по этому указателю вынуть данные. чтобы вынуть элемент массива — просто вынимаешь его по указателю. Что-то похожее со строками, кажется. Ну и еще нету всяких извращений, вроде skip list / unrolled list.

Если std::vector то да, он полностью динамический. В этом и суть. Сишный аналог — структура с указателем на выделенную маллоком память.

Но при необходимости можно пользоваться boost::container::small_vector/static_vector или аналогами.
Да и built-in массивы и std::array никто не отменял.

и вот здесь оказывается, что недостаточно знать C++11(17), STL, еще и Boost из темных дыр вылазит

Вообще ни разу не из тёмных, подход довольно старый. Я работал некоторое время в компании, где была своя собственная реализация стандартной библиотеки — они аналогичные векторы использовали года так с 2005.

Я не о данном конкретном случае, а об объеме знаний и граблей, с которыми необходимо пообщаться, чтобы шарить в «современном С++». И о том, что иногда велосипедописание быстрее и проще, чем библиотекоюзание.

NIH синдром не специфичен для плюсов аж вообще

а об объеме знаний и граблей, с которыми необходимо пообщаться, чтобы шарить в «современном С++»

А кому сейчас легко? :)
Не думаю, что всякие джавы/дотнеты в этом плане проще. Пусть сам core language попроще — зато необходимых библиотек и фреймворков в разы больше, со всеми разобраться задача далеко нетривиальная.
В эмбеддеде можно не париться с плюсами и писать на чистых сях, для которых знаний нужно меньше, — зато нужно очень хорошо шарить в железе, под которое пишешь.
И так далее. Везде свои трудности. Я не думаю, что работать на плюсах настолько тяжелее всего остального.

Плюс, сейчас речь идёт скорее не о знании того, какие классы есть в бусте и как конкретно они называются, а о понимании модели памяти C++ (стек, куча, кеши, вотэтовотвсё) и умении искать информацию под свои нужды.
Например, я программист на плюсах который не знает про boost::container::static_vector. И вот я сталкиваюсь с проблемой: есть узкое место в моей программе, в котором постоянно выделяется/освобождается динамическая память для одного вектора, что негативно влияет на перформанс. Хотя я точно знаю, что количество элементов в этом векторе никогда не превысит 42. Следовательно, понимая модель памяти, я могу без знания о каких-либо библиотеках прийти к выводу, что мне стоит попробовать конструировать эти элементы на стеке.
И вот на этом этапе я пойду в гугл и поищу нужный мне вектор — и, скорее всего, без труда найду инфу о том, что в бусте он уже есть и называется он вот так. Это куда быстрее, чем велосипедить.

И о том, что иногда велосипедописание быстрее и проще, чем библиотекоюзание.

Иногда да. Но далеко не всегда.

Хотя я точно знаю, что количество элементов в этом векторе никогда не превысит 42

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

Вряд ли доступ к статической памяти будет быстрее доступа к объектам на стеке.
Или имеются в виду просто локальные массивы фиксированного (статического) размера?
Если так, то всё зависит от типа объектов. Если там какие-то POD-структуры, которые конструируются бесплатно, то, конечно, с простым массивом будет лучше, не спорю. Но в случае с типами, которые в конструкторе могут что-то «лишнее» делать (или у которых вообще нет дефолтного конструктора), такой подход может не прокатить.

Да вряд ли выделение не-POD объектов будет критическим по времени. По крайней мере, с ходу не могу придумать пример приложения. (Учитывая возможность клонирования и прочий флайвейт)

Да, речь идёт о не-stl массивах фиксированного размера над объектами без конструктора. В циклах с большим количеством итераций разница в перфомансе заметна, за счёт хотябы устранения вызова operator[]. Это я к тому, что в случае если имеет смысл задать статичный массив, вместо динамичного с постоянными реаллоками, то может лучше и вовсе отказаться от STL-контейнера.

Может, лучше. А может разницы и не будет. Всё зависит от используемого контейнера и возможностей компилятора. Всякие вызовы operator[] компилятор может без труда заинлайнить.
Но да, в случае с простыми массивами (если речь идёт о POD-структурах) мы получаем перформанс гарантированно.

А идеальным вариантом на такой случай (по моему скромнейшему мнению) было бы использовать специальный vector-like контейнер для POD-структур, который будет размещать объекты в себе (подобно static_vector’у, но при этом не вызывая конструкторы/деструкторы).
Получим лучшее из обоих миров: и безопасную и удобную в обращении высокоуровневую абстракцию, и лучший перформанс.
Но это в идеальном мире. В реальном, увы, генерируемый асм на пару инструкций отличается: godbolt.org/g/A1Rejk
Хотя не факт, что эта разница будет критичной.

Но это в идеальном мире. В реальном, увы, генерируемый асм на пару инструкций отличается: godbolt.org/g/A1Rejk

Вообще-то, функционал не идентичен. И вообще, почему не взять тогда std::array?

У std::array тот же функционал, что и у built-in массива. И асм он в этом примере даст абсолютно тот же.

Почему не взять простые массивы (built-in или std::array) — потому что я хочу, чтобы фактическое количество используемых элементов было рантаймовой переменной, лежащей внутри контейнера. Чтоб это количество можно было сохранить как size и бегать по этим элементам (в том числе через range-based for цикл), а при попытке вылезти за границы в дебаге ловить фейлы ассертов.

Короче, хочу статическую фиксированную capacity, но не size.
Но при этом так, чтоб работало оно так же быстро, как с простым массивом.
Мне кажется, что с оптимизирующим компилятором это должно быть возможно.

Вообще-то, функционал не идентичен.

Можно поподробнее? Наверное, я что-то упустил.
Почему компилятор на максимальном уровне оптимизаций не генерирует такой же асм для моего велосипедовектора?

Любопытная фигня, кстати.
Если в версии с моим велосипедом заменить range-based for цикл на цикл по индексам от 0 до size и вызовы оператора [] — избавимся от одной строчки
shl r14, 4

Единственное отличие, которое останется — это лишняя инструкция
mov qword ptr [rsp + 1024], r14
(godbolt.org/g/aAjc4N)

Кто бы мне доступно объяснил, почему компилятор её не выбрасывает?)

Поигрался немного. Сори за оффтоп, но вдруг кому будет интересно или, может, кто-то дополнит.

Здесь даже не обязательно иметь шаблон «вектора» — достаточно просто завернуть массив и размер в локальную структуру. И вуаля, появляются лишние записи/чтения в асме: godbolt.org/g/4a94uJ
А если массив в виде отдельной переменной, и размер — отдельной, тогда всё хорошо.

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

Оно даже ожидает от нас подлянки, что вызовы test_consume(wrap.arr[i]) могут изменить значение wrap.arr_size (эта переменная вычитывается из qword ptr [rsp + 1024] после каждой итерации заново).

Это подтверждается тем фактом, что если переписать цикл с индексами как
for (std::size_t i = 0, sz = wrap.arr_size; i < sz; ++i)
— вычитывание «изменённого» размера после каждой итерации уйдёт.
Но запись его в qword ptr [rsp + 1024] перед началом цикла по-прежнему останется. Ну а что, вдруг мы по оффсету от адреса текущего элемента захотим этот сайз хотя бы прочитать... :)

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

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

Если кому-то и правда интересно, велкам ту зис дискашшн ин Велыкобрытаниан ленгвидж он стековерфлоу:
stackoverflow.com/...​to-struct-data-members-as

Могла сработать еще эта штука (C++ draft 2005, clause 5.7.5):
If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined.
На SO лень регистриться

На SO в комментах тоже писали про past the end указатель и специальные правила стандарта, связанные с ним. Но я не думаю, что это тот случай.

См. такой пример: godbolt.org/g/SD1erC (упростил свой предыдущий код ещё больше, полностью избавившись от массива)

Вызов process_value(wrapper.value); всё равно ожидает, что мы из этой функции захотим модифицировать wrapper.size через извращенские реинтерпрет_касты — и поэтому выполняет «честные» запись и чтение вместо использования регистров.

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

ні, може, просто, не настільки одразу треба лізти в ті нові стандарти, а почати з старого доброго C++03.

Я би радив все ж Qt, так як організація контейнерів, та те що «під капотом», а також, «хвілософія КуТе» досить подібна до Java.
До того ж «кросплатформена» та з хрестами (що аффтар і бажав бачити).
А так, голою задницею з джави стибати в С++ я би не радив, очікуваного ефекту швидко не буде, прийдеться кілька років покидати івна лопіто... і можу появиться світло на кінці тунелю.

Так а чим Qt допоможе і відрізняється? Під капотом там десь те саме, що в STL, і далеко не факт, що краще (ловив не інлайнений operator[] там). Ще й донавернули фігні. Там питання «як втурити Qt у мобільний не Qt проект» крові поп’є чимало (це якщо згадати, що ж потрібно).

а для чого його «турити»?
там що IPC відсутнє, сокети-шмокети..?

а для чого його «турити»?

Щоб використовувати. Для того, для чого автору топіка С++ потрібен. Чим IPC допоможе, окремим додатком ставити ще Qt і спілкуватися по IPC із жаба додатком? Мос’є знаток!

В Android-разработке иногда нужно часть приложения писать на C++ с целью улучшения производительности или разработки кроссплатформенного ядра, которое будет также использоваться на iOS: потребность очень даже практическая и осознанная — это нужно мне для дальшейшего развития и повышения ЗП.

мосье пєйсатєль, а не чітатєль.

Мос’є за це гроші отримував) І саме про це пише. Не написати так просто частину додатку на Qt, я взагалі не знаю, як це робиться) Ну, тобто, мабуть зробив би якщо треба, але це навряд легко і є збоченням. Qt ще той монстр.

В Android-разработке иногда нужно часть приложения писать на C++ с целью улучшения производительности или разработки кроссплатформенного ядра, которое будет также использоваться на iOS: потребность очень даже практическая и осознанная — это нужно мне для дальшейшего развития и повышения ЗП.

мосье пєйсатєль, а не чітатєль.

Зачем учить старье, когда можно сразу учить нормальный стандарт?

Потому что старье в разы меньше и понятнее (логичнее, консистентнее)

А по-моему наоборот, начиная с 11х все стало куда логичнее. Пачка removed/deprecated в 17х не просто так.

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

пора вже GC впердячить

Вместо него уже есть смарт пойнтеры, и все сейчас рекомендуют их использовать (goto is considered harmful, arrays are evil, macros are evil, pointers are bad).

так С++ превращаються а жабу, а тут ТС хочуть з неї спригнуть

В современном мире идет отказ от GC, как от тормоза и прожирающего память мусора. Взять тот же swift — там специально при дизайне языка отказались от GC.

И какова альтернатива GC, работать с памятью вручную?

Можно вручную. А можно делегировать эти заботы деструкторам объектов, как это рекомендуют делать в современном C++.

Почему в современном? RAII был там с самого начала, собственно, единственное серьезное отличие С++ от С в деструкторах (вроде, Мейерс рассказывал, что все остальное на С народ спокойно делал).

Согласен, просто в современном появилось ещё больше возможностей для этого. Появились std::unique_ptr; стандартизировали shared_ptr.

И в старом, и в новом, и уже тем более в сях, есть макросы. Там возможностей наговнокодить куда больше.

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

А какие альтернативы? Сделать всё тоже самое, но вручную (linux-kernel-way, glib/gtk-way)? На выходе получиться тоже самое, только больше писанины и еще с парой ошибок.

Часто вручную более читаемо.

Необходимость думать об освобождении ресурсов сильно дисциплинирует в плане читаемости и хорошей структурируемости кода.

Если речь идет о RAII vs manual, то наоборот, гроздья вызовов «деструкторов» (в перемешку либо с if-ами, либо с метками для goto) наоборот ухудшают читабельность. В первую очередь тем, что теперь баге значительно проще скрыться среди одного из неправильных вызовов «деструкторов».

А альтернативой ref-count`у может быть только GC. Собственно, в ядре ляликса ref-count юзается в полную силу.

Речь об RAII vs shared ptrs

гроздья вызовов «деструкторов» (в перемешку либо с if-ами, либо с метками для goto)

Вот-вот, не надо плодить лишние сущности.

Насчёт многопоточности спорно. Она частично стандартизирована начиная с C++11. Если не писать под совсем старые платформы, данный стандарт должен быть доступен, в 2к18-то.
Хотя там нет некоторых нужных вещей, вроде приоритетов потоков, — но для таких случаев стандартная библиотека предоставляет метод native_handle, позволяющий получить (на POSIX-платформах) соответствующий лежащий под капотом pthread_t. Это позволит восполнить некоторые пробелы стандартизации.
А так, для многих задач, связанных с многопоточностью (если гнаться не за перформансом, а за переносимостью), стандартных средств должно хватить.
Я думаю, их человеку, шарящему джаву, будет осваивать куда проще, чем сишные интерфейсы pthread’ов.

Насчёт многопоточности спорно. Она частично стандартизирована начиная с C++11. Если не писать под совсем старые платформы, данный стандарт должен быть доступен, в 2к18-то.

По моему нескромному мнению нет смысла «учить си++ вместо джавы» чтобы использовать «многопоточность в стандарте си++11».

А так, для многих задач, связанных с многопоточностью (если гнаться не за перформансом, а за переносимостью), стандартных средств должно хватить.

Вот именно зачем тогда вообще переходить и «учить» на «плюсы»?

По моему нескромному мнению нет смысла «учить си++ вместо джавы» чтобы использовать «многопоточность в стандарте си++11».

А по моему, it depends. Не вижу смысла обсуждать эту тему без конкретных примеров.

зачем тогда вообще переходить и «учить» на «плюсы»?

Чтобы обходиться стандартными средствами там, где это возможно, прибегая к нестандартным там, где необходимо. В этом есть смысл хотя бы потому, что код, использующий std::thread вместо голых pthread_t, будет намного читабельнее для человека, пришедшего из джавы или дотнета.

А по моему, it depends. Не вижу смысла обсуждать эту тему без конкретных примеров.

В данном конкретном случае конкретный пример конкретно озвучен:

Изучение C++ для Java-разработчика

В Android-разработке иногда нужно часть приложения писать на C++ с целью улучшения производительности или разработки кроссплатформенного ядра, которое будет также использоваться на iOS: потребность очень даже практическая и осознанная — это нужно мне для дальшейшего развития и повышения ЗП.

Недостаточно конкретно, как для обсуждения целесообразности использования std::thread по сравнению с прямым использованием pthread.
Вопрос, зачем вообще использовать треды из C++ при андроид-разработке, я не поднимал. Я прокомментировал этот пункт из ответа выше:

2) многопоточности (вообще не стандартизирована, желательно почитать про линуховый pthread — он более-менее базовый и он должен быть под Андроидом)
разработки кроссплатформенного ядра

JNI цеплять к хрестам (ядро на їх же нада закуячить кросплатформєннимі) ілі как?

Насчёт многопоточности спорно. Она частично стандартизирована начиная с C++11.

Насчет многопоточности всё сложно. Применяемые подходы сильно зависят от задачи.

С этим я и не собирался спорить. Просто сказал, что для некоторых задач вполне может хватить и стандартизированных средств. Которые для человека, пришедшего из джавы или дотнета, будут явно проще в использовании по сравнению с pthreads.

Оно да, если стандартизированных хватает, ну его нафиг лезть в глубины. Кроме того, закатывание солнца ручками через pthread может быть хуже, чем юзание стандартизированного, если ты не дока в многопоточности. STL почти ничего не наворачивает на те же pthreads, но ограничивает стандартным использованием — цена универсализации.

Ну а прокомментил я тебя потому как сама по себе многопоточность сложна, а не юзание STL или pthread или mpi или ...
Причем, самое поганое, если год толком не писал многопоточности, то куча всего забывается.

Подписаться на комментарии