LINUX.ORG.RU

Какую книгу по C++ выбрать?

 ,


5

2

Какую книгу по с++ выбрать

  1. Герберт Шилдт. C++ для начинающих. Шаг за шагом
  2. Стивен Прата. Язык программирования C++. Лекции и упражнения
  3. Предложите свой вариант (учил джаву, с++ смотрел видео ну можно сказать не с нуля учу)

Перемещено hobbit из general

Ответ на: комментарий от bugfixer

Покажите мне этот сегфолт. Хинт: capacity != size, нужно сильно постараться чтобы этот memwrite чужую память попортил.

Хм.

#include <iostream>
#include <vector>
#include <csignal>

int i;

void signalHandler( int signum ) {
//Обработчик SIGSEGV
   std::cerr << "Segmentation fault detected!" << std::endl;
   std::cerr <<  "i = " << i << std::endl;   
   std::exit(signum); 
}
 
int main(void)
{

    std::signal(SIGSEGV, signalHandler);
    
    std::vector<int> v = { 1, 2, 3};
    i = v[v.size()-1];
    while (i++)
    {
       v[i]=i; //Ждем когда вывалится с исключением.
    }
    return 0;
}

Запускаем:

$ g++ -o segf segf.cpp
$
$ ./segf
Segmentation fault detected!
i = 15444

Похоже за размер стека перевалило.

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

А современность С++ погоды в целом не делает в этом вопросе вообще никакой.

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

C++98 уже разительно отличался от C++ с которого я начинал в 1992-м. А C++11 еще более. Чем дальше, тем больше приходится пользоваться именно концепцией итераторов, а не указателями.

Например, я вот честно ХЗ зачем здесь пример кода на C#, но попробуйте его применить к контейнеру, который хранит элементы последовательно, но не в одном непрерывном векторе, а в наборе чанков. В C++ можно сделать контейнер, который выставляет наружу итераторы и тот же std::lower_bound будет с ним работать.

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

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

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

я вот честно ХЗ зачем здесь пример кода на C#,

Обобщённый алгоритм в языке, где нет никакой адресной арифметики.

Ну так давайте разберем какие-то конкретные примеры.

Я выискивать из прода щас ничего не буду, возьму простейший пример даже без параметризации форматом матрицы:

struct img
{
    size_t width;
    size_t height;
    unsigned char *data; // BGR
};

extern void f(size_t x, size_t y, unsigned char r, unsigned char g, unsigned char b);

void process_bitmap(struct img &img)
{
    for(size_t y = 0; y < img.height; y++)
    {
        for(size_t x = 0; x < img.width; x++)
        {
            unsigned char *p = img.data + img.width*3*y + x*3;
            f(x, y, p[2], p[1], p[0]);
        }
    }
}

как это эффективно сделать без адресной арифметики и алиасинга?

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

unsigned char p = img.data + img.width3y + x3;

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

img.data + img.width3y

эта лабуда не меняется при цикле по х. и умножение x*3 избыточно.

у настоящих посонов цикл по x должен выглядеть так:

unsigned char* p  = img.data + img.width*3*y; ///начало
unsigned char* pp = p + img.width*3; ///конец
while ( p < pp) {
  f(x, y, p[2], p[1], p[0]);
  p += 3; 
}

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

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

Обобщённый алгоритм в языке, где нет никакой адресной арифметики.

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

как это эффективно сделать без адресной арифметики и алиасинга?

Хороший пример. Но зачем для этого знать Си и почему нельзя это разбирать сразу в C++? В Си какая-то другая адресная арифметика?

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

Какого ещё стэка?

Показалось, что небольшая переменная на стеке размещена

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

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

Обобщённый алгоритм в языке, где нет никакой адресной арифметики.

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

С чего бы? Оператор [] прекрасно переопределяется. Код из примера выше почти полная копия std::binary_search. Чтобы сделать его полной копией достаточно добавить функцию - компаратор.

Это пример того, что в этом объёме выразительные возможности C# и C++ практически идентичны. Разница между ними, конечно, есть, но уже в других деталях.

Но зачем для этого знать Си

Мы уже ушли с этого вопроса на вопрос "а зачем вообще нужна адресная арифметика?".

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

С чего бы?

А разве прототип метода не указывает прямо на тип входной последовательности?

public int FindIndex(T[] sortedData, T item)

Мы уже ушли с этого вопроса на вопрос «а зачем вообще нужна адресная арифметика?».

Может покажете место, где это произошло, потому что я не помню чтобы где-то утверждал, что адресная арифметика не нужна.

В современном C++ дело с ней приходится иметь реже, чем раньше (мне, например, в своей работе очень редко). Но под сомнение я эту часть C++ не ставил.

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

v[v.size()] = 0;

полностью эквивалентна записи:

*(v.begin() + v.size()) = 0;

И в таком варианте она применима не только к std::vector, но и к другим типам, например, к std::deque.

Т.е. чтобы объяснить изучающему C++ суть проблемы в этом случае не нужно опускаться на уровень адресной арифметики.

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

запись: v[v.size()] = 0; полностью эквивалентна записи: *(v.begin() + v.size()) = 0;

И в таком варианте она применима не только к std::vector, но и к другим типам

У других типов может быть другая семантика оператора []

#include <map>
void test() {
    std::map<size_t, int> list;
    auto begin{ list.begin()};
    // begin + list.size(); // ??
    list[list.size()];
}
vM ★★
()
Ответ на: комментарий от vM

Когда я писал «И в таком варианте она применима не только к std::vector, но и к другим типам», то речь шла именно о записи:

*(v.begin() + v.size()) = 0;

Кроме того, «она применима не только к std::vector, но и к другим типам» не означает, что применима ко всем другим типам.

Так что решительно непонятно что именно вы хотели сказать своим комментарием.

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

С чего бы?

А разве прототип метода не указывает прямо на тип входной последовательности?

Ровно в той же, что мере, что и std::binary_search - обобщённая функция принимает на вход контейнер элементов шаблонного типа T и элемент шаблонного типа T, который ищет в контейнере. Шаблонный тип T должен удовлетворять интерфейс IComparable - это полный аналог наличия оператора std::less для C++, с той только разницей, что в случае ошибки вместо загадочной простыни на 1000 строк про отсутствие оператора в месте вызова тут будет выдана корректная ошибка.

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

Ровно в той же, что мере, что и std::binary_search

Вы точно уверены на счет «принимает на вход контейнер»?

https://en.cppreference.com/w/cpp/algorithm/binary_search.html

Я не знаток C#, но разве запись T[] означает произвольный контейнер с наличием оператора []?

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

А что ещё она может означать?

Что-то вроде аналога C++ного span, т.е. непрерывную последовательность из N элементов.

Повторюсь, я не знаток C#, но неужели T[] – это эквивалент произвольного IEnumerable?

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

неужели T[] – это эквивалент произвольного IEnumerable?

T[] - это экземпляр класса array параметризированый шаблонным типом T. Такой класс может быть создан пользователем через indexer - я же выше писал:

Оператор [] прекрасно переопределяется.

Array реализует несколько интерфейсов, среди них и IEnumerable, так что, да, одномерный T[] может быть использован везде, где используется IEnumerable.

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

А зачем?

Я это к тому, что если бы у вас в примере было:

int FindIndex(IEnumerable<T> sortedData, T item)

То это было бы более похоже на C++ный std::binary_search. А так сравнение не равноценное.

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

Сишарпный IEnumerable<T> в переводе на сиплюсплюснотый язык - это прямой итератор чтения / записи без произвольного доступа. В столь любимом вами "современном С++" это комбинация трейтов input_iterator / output_iterator / forward_iterator.

А для бинарного поиска нам нужен трейт random_access_iterator, что в сишарпе предоставляется оператором [].

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

С плюсами такая штука - читай всё, что попадётся под руку. Язык довольно разлапистый, и для нормального знакомства потребуется не одна книжка. Классики: Страуструп, Мейерс, Йосутис. Если что-то непонятно, спрашивай у chatGPT, и проверяй ответы экспериментально. И Шилда, и Прату бери, нос не вороти. Отзывы в инете читай, но учти, что люди частенько хотят идеальных книг, а такого не бывает.

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

Вот я, например, сейчас читаю классическую механику, Cambridge press, казалось бы не хухры-мухры, книга 2006г., 6я перепечатка. И там во вводной части о тройном векторном произведении, как минимум в одном месте такая чушь написана, что впору этот кэмбридж пресс использовать для розжиги печки. И это естественнонаучная дисциплина. Что же говорить о интеллектуально менее развитых «специалистах по программированию»…

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

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

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

Вы же тут обсуждаете про научению ЯЗЫКУ. Вспомните, что РОДНОЙ язык на достойный уровень владения учат 15 лет с АГРОМЕННОЙ кучей «чтения и написания кода». Но овладевают им не только лишь все.

«Теория без практики мертва» – абсолютная истина при изучении ЛЮБОГО языка, от математики и латыни до Rust или Common Lisp. Чтобы научиться писать на ЯП, необходимо научиться ДУМАТЬ на этом языке, что невозможно без его использования.

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

mister_VA ★★
()