Функция all_of
Эта функция работает со всем диапазоном элементов массива и может сэкономить время на запуск цикла для проверки каждого элемента по одному. Она проверяет заданное свойство для каждого элемента и возвращает true, когда каждый элемент в диапазоне удовлетворяет указанному свойству, иначе возвращает false.
В приведенном выше коде отрицательный элемент -6 отрицает условие и возвращает false.
➡️ @cpp_geek
Эта функция работает со всем диапазоном элементов массива и может сэкономить время на запуск цикла для проверки каждого элемента по одному. Она проверяет заданное свойство для каждого элемента и возвращает true, когда каждый элемент в диапазоне удовлетворяет указанному свойству, иначе возвращает false.
В приведенном выше коде отрицательный элемент -6 отрицает условие и возвращает false.
➡️ @cpp_geek
Cтрого-типизированный enum
У «традиционных» перечислений в С++ есть некоторые недостатки: они экспортируют свои значения в окружающую область видимости (что может привести к конфликту имен), они неявно преобразовываются в целый тип и не могут иметь определенный пользователем тип.
Эти проблемы устранены в С++11 с введением новой категории перечислений, названных strongly-typed enums. Они определяются ключевым словом enum class. Они больше не экспортируют свои перечисляемые значения в окружающую область видимости, больше не преобразуются неявно в целый тип и могут иметь определенный пользователем тип (эта опция так же добавлена и для «традиционных» перечислений").
➡️ @cpp_geek
У «традиционных» перечислений в С++ есть некоторые недостатки: они экспортируют свои значения в окружающую область видимости (что может привести к конфликту имен), они неявно преобразовываются в целый тип и не могут иметь определенный пользователем тип.
Эти проблемы устранены в С++11 с введением новой категории перечислений, названных strongly-typed enums. Они определяются ключевым словом enum class. Они больше не экспортируют свои перечисляемые значения в окружающую область видимости, больше не преобразуются неявно в целый тип и могут иметь определенный пользователем тип (эта опция так же добавлена и для «традиционных» перечислений").
➡️ @cpp_geek
Что за оператор −−> в С++?
Это старый хитрый вопрос. В С++ нет оператора −−>.
Рассмотрим такой код:
if (p−−>m == 0) f(p);
Выглядит так, как будто и правда есть оператор −−>, и если правильно объявить переменные p и m, то код даже скомпилируется и запустится:
int p = 2;
int m = 0;
if (p−−>m == 0) f(p);
Это означает: если p−− больше чем m (а это так), то надо сравнить результат (true) с нулём. Ну, true != 0, так что результат всего выражения — false, и функция f() не вызовется. Другими словами:
if ((p−−) > m == 0) f(p);
Пожалуйста, не тратьте много времени на подобные вопросы. Они сбивали с толку новичков ещё до того, как появился С++.
➡️ @cpp_geek
Это старый хитрый вопрос. В С++ нет оператора −−>.
Рассмотрим такой код:
if (p−−>m == 0) f(p);
Выглядит так, как будто и правда есть оператор −−>, и если правильно объявить переменные p и m, то код даже скомпилируется и запустится:
int p = 2;
int m = 0;
if (p−−>m == 0) f(p);
Это означает: если p−− больше чем m (а это так), то надо сравнить результат (true) с нулём. Ну, true != 0, так что результат всего выражения — false, и функция f() не вызовется. Другими словами:
if ((p−−) > m == 0) f(p);
Пожалуйста, не тратьте много времени на подобные вопросы. Они сбивали с толку новичков ещё до того, как появился С++.
➡️ @cpp_geek
nullptr
Раньше, для обнуления указателей использовался макрос NULL, являющийся нулем — целым типом, что, естественно, вызывало проблемы (например, при перегрузке функций). Ключевое слово nullptr имеет свой собственный тип std::nullptr_t, что избавляет нас от бывших проблем. Существуют неявные преобразования nullptr к нулевому указателю любого типа и к bool (как false), но преобразования к целочисленных типам нет.
➡️ @cpp_geek
Раньше, для обнуления указателей использовался макрос NULL, являющийся нулем — целым типом, что, естественно, вызывало проблемы (например, при перегрузке функций). Ключевое слово nullptr имеет свой собственный тип std::nullptr_t, что избавляет нас от бывших проблем. Существуют неявные преобразования nullptr к нулевому указателю любого типа и к bool (как false), но преобразования к целочисленных типам нет.
➡️ @cpp_geek
Инициализатор в if и switch
Вам должна понравиться такая возможность из С++17. Теперь вы можете выполнять инициализацию переменных и проверять условие внутри if или switch. Это даёт сделать код более лаконичным и чистым. Общая форма:
if (init-statement(x); condition(x)) {
// some code
}
else { // в else тоже видно x
// some more code
}
➡️ @cpp_geek
Вам должна понравиться такая возможность из С++17. Теперь вы можете выполнять инициализацию переменных и проверять условие внутри if или switch. Это даёт сделать код более лаконичным и чистым. Общая форма:
if (init-statement(x); condition(x)) {
// some code
}
else { // в else тоже видно x
// some more code
}
➡️ @cpp_geek
Что такое класс хранения?
Класс, который определяет срок существования, компоновку и расположение переменных/функций в памяти.
В C ++ поддерживаются такие классы хранения: auto, static, register, extern и mutable.
Обратите внимание, что register устарел для C++11. Для C++17 он был удален и зарезервирован для будущего использования.
➡️ @cpp_geek
Класс, который определяет срок существования, компоновку и расположение переменных/функций в памяти.
В C ++ поддерживаются такие классы хранения: auto, static, register, extern и mutable.
Обратите внимание, что register устарел для C++11. Для C++17 он был удален и зарезервирован для будущего использования.
➡️ @cpp_geek
Кортежи
Как и пара, кортеж представляет собой набор значений фиксированного размера для различных типов данных.
Иногда удобнее использовать std::array вместо кортежа. Такой массив подобен обычному массиву в Си вместе с несколькими функциями стандартной библиотеки C++. Эта структура данных была добавлена в 11 версии C++.
➡️ @cpp_geek
Как и пара, кортеж представляет собой набор значений фиксированного размера для различных типов данных.
Иногда удобнее использовать std::array вместо кортежа. Такой массив подобен обычному массиву в Си вместе с несколькими функциями стандартной библиотеки C++. Эта структура данных была добавлена в 11 версии C++.
➡️ @cpp_geek
this удобно использовать для цепочных вызовов
Мы можем вернуть ссылку на объект, на котором мы вызываем метод класса:
Foo &set(int x) { this->x = x; return *this; }
Здесь метод set возвращает ссылку на объект класса Foo(Foo&). Такая реализация метода позволяет нам писать код, подобный этому:
obj.set(2).set(8);
Это возможно, поскольку первый вызов метода set возвращает указатель на объект obj. Второй метод вызывается на объекте obj, т. е. можно переписать код так:
obj = obj.set(2);
obj = obj.set(8);
➡️ @cpp_geek
Мы можем вернуть ссылку на объект, на котором мы вызываем метод класса:
Foo &set(int x) { this->x = x; return *this; }
Здесь метод set возвращает ссылку на объект класса Foo(Foo&). Такая реализация метода позволяет нам писать код, подобный этому:
obj.set(2).set(8);
Это возможно, поскольку первый вызов метода set возвращает указатель на объект obj. Второй метод вызывается на объекте obj, т. е. можно переписать код так:
obj = obj.set(2);
obj = obj.set(8);
➡️ @cpp_geek
Различие локальной переменной и поля класса с одинаковым именем
Указатель this может быть полезен в случае, когда локальная переменная в методе имеет то же самое имя, что и поле объекта:
void set(int x) { this->x = x; }
Здесь в методе set мы присваиваем полю класса x значение локальной переменной this. Чтобы различить поле класса x и локальную переменную с тем же именем мы используем запись this->x при обращении к полю класса.
➡️ @cpp_geek
Указатель this может быть полезен в случае, когда локальная переменная в методе имеет то же самое имя, что и поле объекта:
void set(int x) { this->x = x; }
Здесь в методе set мы присваиваем полю класса x значение локальной переменной this. Чтобы различить поле класса x и локальную переменную с тем же именем мы используем запись this->x при обращении к полю класса.
➡️ @cpp_geek
Что вообще означает модификатор virtual?
В C++ виртуальные функции позволяют поддерживать полиморфизм – одну из ключевых составляющих ООП. С его помощью в классах-потомках можно переопределять функции класса-родителя. Без виртуальной функции мы получаем «раннее связывание», а с ней – «позднюю привязку». То есть, какая реализация метода используется, определяется непосредственно во время выполнения и основывается на типе объекта с указателем на объект, из которого он построен.
➡️ @cpp_geek
В C++ виртуальные функции позволяют поддерживать полиморфизм – одну из ключевых составляющих ООП. С его помощью в классах-потомках можно переопределять функции класса-родителя. Без виртуальной функции мы получаем «раннее связывание», а с ней – «позднюю привязку». То есть, какая реализация метода используется, определяется непосредственно во время выполнения и основывается на типе объекта с указателем на объект, из которого он построен.
➡️ @cpp_geek
Работа с файлами
Например, необходимо создать текстовый файл и записать в него строку "Работа с файлами в С++". Для этого необходимо проделать следующие шаги:
1) создать объект класса ofstream;
2) связать объект класса с файлом, в который будет производиться запись;
3) записать строку в файл;
4) закрыть файл.
➡️ @cpp_geek
Например, необходимо создать текстовый файл и записать в него строку "Работа с файлами в С++". Для этого необходимо проделать следующие шаги:
1) создать объект класса ofstream;
2) связать объект класса с файлом, в который будет производиться запись;
3) записать строку в файл;
4) закрыть файл.
➡️ @cpp_geek
Перегрузка операторов
Ключевое слово operator позволяет изменить работу конкретного оператора с экземплярами класса. Это дает оператору дополнительное значение — "перегружает" его. Компилятор различает разные значения оператора, проверяя типы его операндов.
В примере оператор + перегружается для сложения двух комплексных чисел.
➡️ @cpp_geek
Ключевое слово operator позволяет изменить работу конкретного оператора с экземплярами класса. Это дает оператору дополнительное значение — "перегружает" его. Компилятор различает разные значения оператора, проверяя типы его операндов.
В примере оператор + перегружается для сложения двух комплексных чисел.
➡️ @cpp_geek
this является const указателем
Указатель this является неизменяемым указателем. Убедимся это на примере:
class Foo {
private:
int x;
public:
Foo(int x = 0) { this->x = x; }
void change(Foo *foo) { this = foo; }
void print() { cout << x << endl; }
};
Здесь в методе change мы пытаемся присвоить указателю this новое значение.
Попробуем вызвать метод change в методе main:
Foo obj (3);
Foo *ptr;
obj.change(ptr);
obj.print();
Если мы запустим код, то получим ошибку компиляции в методе change (lvalue required as left operand of assignment). Компилятор не позволяет нам изменить значение указателя this.
➡️ @cpp_geek
Указатель this является неизменяемым указателем. Убедимся это на примере:
class Foo {
private:
int x;
public:
Foo(int x = 0) { this->x = x; }
void change(Foo *foo) { this = foo; }
void print() { cout << x << endl; }
};
Здесь в методе change мы пытаемся присвоить указателю this новое значение.
Попробуем вызвать метод change в методе main:
Foo obj (3);
Foo *ptr;
obj.change(ptr);
obj.print();
Если мы запустим код, то получим ошибку компиляции в методе change (lvalue required as left operand of assignment). Компилятор не позволяет нам изменить значение указателя this.
➡️ @cpp_geek
Библиотечный метод
Продемонстрируйте работу метода сортировки вставками по возрастанию. Для этого выведите состояние данного массива после каждой вставки на отдельных строках. Если массив упорядочен изначально, не нужно ничего выводить.
Формат входных данных:
На первой строке дано целое число n (1 ≤ n ≤ 100) – количество элементов в массиве. На второй строке задан сам массив: последовательность натуральных чисел, не превышающих 10^9.
Формат выходных данных:
В выходной файл выведите строки (по количеству вставок) по n чисел каждая.
➡️ @cpp_geek
Продемонстрируйте работу метода сортировки вставками по возрастанию. Для этого выведите состояние данного массива после каждой вставки на отдельных строках. Если массив упорядочен изначально, не нужно ничего выводить.
Формат входных данных:
На первой строке дано целое число n (1 ≤ n ≤ 100) – количество элементов в массиве. На второй строке задан сам массив: последовательность натуральных чисел, не превышающих 10^9.
Формат выходных данных:
В выходной файл выведите строки (по количеству вставок) по n чисел каждая.
➡️ @cpp_geek
Общие хитрости для C++
Никогда не используйте INT_MAX в качестве бесконечности для целых чисел. В некоторых алгоритмах, например, Флойда–Уоршелла, используются значения вроде ∞+w, что приведёт к переполнению при использовании INT_MAX. Вместо этого лучше использовать int oo = 0x3f3f3f3f, поскольку:
• Это число достаточно большое для задач, связанных с целыми числами;
• 2 * oo не приведёт к переполнению;
• Все байты равны, поэтому вы без проблем можете использовать memset(array, oo, sizeof(array));
• Его довольно легко запомнить.
Однако будьте осторожны: не используйте 0x3f3f3f3f для long long, так как в таком случае фокус уже не пройдёт, и вы потом потратите кучу времени на поиск ошибки.
➡️ @cpp_geek
Никогда не используйте INT_MAX в качестве бесконечности для целых чисел. В некоторых алгоритмах, например, Флойда–Уоршелла, используются значения вроде ∞+w, что приведёт к переполнению при использовании INT_MAX. Вместо этого лучше использовать int oo = 0x3f3f3f3f, поскольку:
• Это число достаточно большое для задач, связанных с целыми числами;
• 2 * oo не приведёт к переполнению;
• Все байты равны, поэтому вы без проблем можете использовать memset(array, oo, sizeof(array));
• Его довольно легко запомнить.
Однако будьте осторожны: не используйте 0x3f3f3f3f для long long, так как в таком случае фокус уже не пройдёт, и вы потом потратите кучу времени на поиск ошибки.
➡️ @cpp_geek
Сколько в памяти занимает произвольная структура?
Ответ: sizeof всех членов + остаток для выравнивания (по умолчанию выравнивание 4 байта) + sizeof указателя на vtable (если есть виртуальные функции) + указатели на классы предков, от которых было сделано виртуальное наследование (размер указателя * количество классов).
➡️ @cpp_geek
Ответ: sizeof всех членов + остаток для выравнивания (по умолчанию выравнивание 4 байта) + sizeof указателя на vtable (если есть виртуальные функции) + указатели на классы предков, от которых было сделано виртуальное наследование (размер указателя * количество классов).
➡️ @cpp_geek
Введение в обработку исключений
Одним из преимуществ C++ перед C является обработка исключений. Исключения - это ненормальные условия, с которыми программа сталкивается во время своего выполнения.
Есть два типа исключений: а) синхронные, б) асинхронные (например, которые находятся вне контроля программы, сбой диска и т. д.). Для этой цели C++ предоставляет следующие специализированные ключевые слова.
try: представляет блок кода, который может вызвать исключение.
catch: представляет блок кода, который выполнится при возникновении определенного исключения.
throw: используется для создания исключения. Также используется для перечисления исключений, которые генерирует функция, но не обрабатывает себя.
➡️ @cpp_geek
Одним из преимуществ C++ перед C является обработка исключений. Исключения - это ненормальные условия, с которыми программа сталкивается во время своего выполнения.
Есть два типа исключений: а) синхронные, б) асинхронные (например, которые находятся вне контроля программы, сбой диска и т. д.). Для этой цели C++ предоставляет следующие специализированные ключевые слова.
try: представляет блок кода, который может вызвать исключение.
catch: представляет блок кода, который выполнится при возникновении определенного исключения.
throw: используется для создания исключения. Также используется для перечисления исключений, которые генерирует функция, но не обрабатывает себя.
➡️ @cpp_geek