Ответы пользователя по тегу C++
  • Является ли хорошим решением разбивать большой класс на несколько .cpp файлов (C++)?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Это очень плохая идея. Теперь пользователю придется искать функцию в 5 файлах, потому что она может быть в любом из них. Если же куски класса можно как-то логически сгруппировать, то надо разбить сам класс на несколько более мелких. Вынести часть функционала в вспомогательные классы или даже тупо функции.

    Большой файл - гораздо менее вредно, чем нелогичная файловая структура проекта. Вот большие функции и класс с излишне большой ответственностью - это плохо. А файл - фигня. Строк от разбивки меньше не становится. Ну придется еще переключатся между файлами - только хуже станет.
    Ответ написан
    Комментировать
  • Как отключить определение функции через шаблоны?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Можно сделать так:

    template <int a, int b>
    class A {
        public:
        template<bool tmp = true>
        typename std::enable_if<a==b && tmp, int>::type F() {
           return 1;   
        }
    };
    
    ...
    
    A<1,1> a;
    std::cout << a.F();  // OK.
    A<2,100> b;
    std::cout << b.F(); // Ошибка A<2,100> не имеет метода F().


    Мне сложно объяснить, чтобы было понятно, почему работает именно это, но я попробую.

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

    Далее, хотелось бы что бы просто работало вот это:
    template<>
     typename std::enable_if<a==b, int>::type F()

    Тут все понятно, enable_if не имеет type, если a неравно b. Шаблон никак не инстанциировать и метод не должен был бы генерироваться. Но это было бы слишком просто.

    Вместо этого, надо там завести какой-то вообще ненужный как бы параметр шаблона tmp, и обязательно использовать его в enable_if. Это потому что если в этом шаблоне не будет никак использоваться параметр шаблона, то SFINAE не срабатывает, и вылезает ошибка компиляции.
    Ответ написан
  • Будет ли std::swap(vector[0], vector[1]) быстрее, чем vector[1] = vector[0]?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    В общем случае - будет.
    std::swap работает за константу:
    Complexity
    1) Constant.


    Присвоение за линию:
    Complexity
    1) Linear in the size of *this and other.


    Edit: это если присваемые штуки - вектора. Или какие-то большие объекты с семантикой перемещения. Если у вас тупо числа, то одно присвоение будет быстрее swap.
    Ответ написан
  • Как с помощью шаблонов проверить, что два числа равны?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Отвечаю на вопрос из комментариев. Можно сделать так:

    template <int a, int b>
    class A {
        public:
        template<bool tmp = true>
        typename std::enable_if<a==b && tmp, int>::type F() {
           return 1;   
        }
    };
    
    ...
    
    A<1,1> a;
    std::cout << a.F();  // OK.
    A<2,100> b;
    std::cout << b.F(); // Ошибка A<2,100> не имеет метода F().


    Мне сложно объяснить, чтобы было понятно, почему работает именно это, но я попробую.

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

    Далее, хотелось бы что бы просто работало вот это:
    template<>
     typename std::enable_if<a==b, int>::type F()

    Тут все понятно, enable_if не имеет type, если a неравно b. Шаблон никак не инстанциировать и метод не должен был бы генерироваться. Но это было бы слишком просто.

    Вместо этого, надо там завести какой-то вообще ненужный как бы параметр шаблона tmp, и обязательно использовать его в enable_if. Это потому что если в этом шаблоне не будет никак использоваться параметр шаблона, то SFINAE не срабатывает, и вылезает ошибка компиляции.
    Ответ написан
    Комментировать
  • Как дополнить двумерную матрицу нулями по краям?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Сначала каждую строку resize до размера size()+w0+w1. Потом сдвигайте элементы на w0 позиций вправо (цикл надо гнать с конца). Потом заполните пустые места нулями.

    То же самое в самом векторе векторов. Только вместо чисел надо сдвигать вектора на h0 позиций. На пустые места надо будет записывать std::vector(n+w0+w1, 0);

    Edit: А вообще, тут и добавлять в массив ничего не надо. Вы нужный вам ответ можете нулями прямо во время вывода добить.
    Ответ написан
    Комментировать
  • Как разбить объявление класса и его реализацию?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Вообще тут есть другая проблема, с которой вы столкнетесь. Если у вас определение шаблона и реализация в разных файлах, то будут ошибки на этапе линковки. Компилятор, когда будет обрабатывать cpp файл не увидит никаких использований шаблона и с чистым сердцем не сгенерирует никаких его специализаций. Потом линкер не сможет найти нужную реализацию и ругнется.

    Чтобы это исправить нужно или засунуть все в h файл, или в cpp файле прописать специализации шаблона со всеми типами, которые где-либо еще используются, вот так:
    using A<int>;
    using A<double>;
    // etc


    Далее, как именно и зачем вы хотите этот внутренний класс скрыть?
    Иногда вам может быть достаточно сделать член этого типа private и все. Факт того, что кто-то где-то сможет завести переменную этого типа обычно не является проблемой. Для списка уж точно. Если же это проблема и внутренний класс является friend внешнего и что-то такое делает, что нарушает инкапсуляцию (например при учнитожении, что-то меняет в родительском классе), то просто сделав вложенный класс private, вы ничего не добъетесь. Переменные этого типа все-равно можно будет использовать и к ним обращатся (иногда через хаки). Обычно у внутренних классов просто делают приватным конструктор и нужные методы и прописывают внешний класс как friend. Тогда вы в самом классе сможете переменные этого типа создавать и использовать, но никто снаружи ничего с ним сделать не может.

    И кстати, в вашем случае вам совсем нет смысла делать вложенный класс шаблоном. Он уже в шаблоне же, просто используйте внутри тип T.
    Ответ написан
    Комментировать
  • Как передать list в функцию c++?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    В параметрах функции:
    list ItemsList
    но в другом месте:
    list <int> ItemsList

    list - это шаблон или как?
    Ответ написан
  • Проверить первые 2 байта?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Окройте файл в ifstream в бинарном режиме и читайте 2 байта через read.

    Или используйте fread. Читайте 2 байта в буфер длинной 2.
    Ответ написан
    Комментировать
  • Рекурсия.По какой причине ответ всегда 0?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Аккуратно расставьте отступы. У вас там логика if else напутана, и программа делает совсем не то, что бы вам хотелось. Кроме того, посмотрите внимательно на warning-и от компилятора (он точно скажет, что power иногда не возвращает никаких значений, хотя вам кажется, что return есть во всех ветках if else). Это как раз потому, что у вас скобки не так расставлены и else относится совсем не к тому if, как вы хотели бы.
    Ответ написан
    Комментировать
  • Зачем в стандартной библиотеке вначале класса объявлены псевдонимы для параметров шаблона?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Это делается по нескольким причинам.

    Во-первых, чтобы можно было к параметру шаблона обращатся в других шаблонах. Вот, вы привели пример с _Сontainer в каком-то итераторе. Можно представить что есть какой-то алгоритм (функция), который принимает итератор и ему там надо будет как-то использовать тип контейнера (или временную переменную завести, или тип возвращаемого значения задать или как-то проверить, что тип контейнера обладает какими-то свойствами).

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

    Во-вторых, иногда это делается и для удобства. Ибо в этих шаблонах параметры сами могут быть адски нагроможденными шаблонами.
    Ответ написан
    Комментировать
  • Как сравнить 2 класса с разными шаблонными параметрами?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    А не проще ли так?
    template <const std::size_t other_columns>
     Matrix<rows, other_columns, T> MultiplyOnMatrix(const Matrix<columns, other_columns, T>& matrix)
    Ответ написан
    1 комментарий
  • Как перегрузить оператор += для двух массивов?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    У вас нет проблем с перегрузкой. У вас там проблема с логикой программы. Для начала объясните себе, что хранит в себе класс Array (зачем там 2 массива a и b?), что должен делать опреатор += для двух экземпляров Array?
    Ответ написан
    Комментировать
  • Почему можно обратиться к объекту через указатель со значением nullptr?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Метод класса под капотом - это функция, котрой помимо прописанных параметров передается указатель на объект. В вашем случае передается nullptr. Но сама функция не обращается к объекту, да у него и членов-то с данными никаких нет. Поэтому все более менее работает. Но это UB и могло и не повезти. Из-за оптимизаций компилятор мог бы что-то сломать.

    P.s. сказанное выше не относится к виртуальным функциям. Там указатель на функцию храниться в vtable в экземпляре класса и вызов через nullptr скорее всего упадет.
    Ответ написан
    Комментировать
  • Почему в СТЕКЕ разрешается выделять достаточно мало памяти?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Да, большинство ресурсов выделяется динамически.

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

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

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

    Edit: да, если вам нужно больше стека, можно сделать так, чтобы программа получала больше стека. В С++ это делается параметром линкера.
    Ответ написан
    Комментировать
  • Как реализовать copy on write class?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Сам класс хранит умный указатель на буфер данных.

    Нужен счетчик ссылок на буфер. Перед записью проверяйте, что счетчик равен 1. Иначе копируете данные и в текущем объекте заменяете указатель на новый буфер.

    Счетчик должен храниться где-то вместе с буфером и является частью умного указателя.

    Можно использовать std::shared_ptr - там уже этот счетчик реализован (use_count).
    Ответ написан
    Комментировать
  • Почему в массив записываются числа не и указанного диапазона?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    *p = (rand( )%100 - 50);
    for (int i = 1; i < k; i++) {
      *(p + 1) = -i * * (p + i - 1);
    }


    Можете вообще объяснить, что этот код делает? Зачем он это делает? Что вы хотели, чтобы он делал?

    Помимо этого, вместо *(p+X) можно писать p[X]. Так понятнее. У вас тут явно не к месту арифметика указателей приплетена.

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

    Поскольку массив у вас локальный, то он нулями не инициализируется. Поэтому там изначально случайный мусор. Поэтому все, кроме первого элемента так и будет случайным мусором.
    Ответ написан
  • Как правильно освобождать память?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    В деструкторе удаляйте, только если память не nullptr или длина не 0.
    Ответ написан
    Комментировать
  • Из за чего появляется ошибка ошибка ld returned 1 exit status?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Выше написанно undefined reference to 'processingArray(int*, int)'


    Ну вот. У вас функция processingArray объявлена, но нигде не реализована. Тело функции где? Поскольку вы ее объявили, компилятор это безобразие проглатил и понадеялся, что эта функция реализована в другом файле и на этапе линковки она найдется. Но линкер ее не нашел, потому что ее нигде и нет.

    Вы, видимо, не все из учебника переписали. Там после функции main скорее всего идет реализация функции processingArray.
    Ответ написан
    7 комментариев
  • Как решить проблему Segmention Fault?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Segmentation fault - это ошибка работы с памятью. Или выход за границы массива, или что-то не так с указателями.

    Я подозреваю, что в файле у вас не 50 строк (мне лень считать). Вот вы и вылезаете за границу массива.

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

    Советую вместо define N и статического массива использовать std::vector. В цикле считывания файла делайте push_back нового объекта в вектор (а еще лучше, emplace_back для экономии лишнего копирования).

    При выводе берите size() у вашего вектора.

    Если вы в выводе заметите лишнюю книгу в конце с пустыми данными, то можно добавить проверку при вводе - если хоть одна из 5 строк пустая - то не надо создавать новую книгу.
    Ответ написан
    Комментировать
  • Как составлять header файл для статической библиотеки?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    А зачем это делать? Чем вас 8 хедеров не устраивают? Если уж так хочется сэкономить строчки в коде пользователей библиотеки, то можно воспользоваться рекурсивностью препроцессора и сделать хедер с 8 инклудами. Копипастить код точно не надо.
    Ответ написан
    1 комментарий