Источники:
char c = '1'; // символ bool b = true; // логическая переменная, принимает значения false и true int i = 42; // целое число (занимает, как правило, 4 байта) short int i = 17; // короткое целое (занимает 2 байта) long li = 12321321312; // длинное целое (как правило, 8 байт) long long lli = 12321321312; // длинное целое (как правило, 16 байт) float f = 2.71828; // дробное число с плавающей запятой (4 байта) double d = 3.141592; // дробное число двойной точности (8 байт) long double ld = 1e15; // длинное дробное (как правило, 16 байт)
По умолчанию типы знаковые, необходимо использовать "unsigned" для получения беззнаковых типов.
unsigned int ui = 4294967295; // 2^32 - 1
Размер типа и минимальное-максимальное значение:
#include <limits> // необходимо для numeric_limits int main() { std::cout << "long long int: " << sizeof(long int) << "\n"; // для типа int: std::cout << "minimum value: " << std::numeric_limits<int>::min() << "\n" << "maximum value: " << std::numeric_limits<int>::max() << "\n"; }
Размер long int на 64-разрядной машине составляет 8 байт, а на 32-разрядной машине 4 байта.
Типы с фиксированным размером, независящим от битности системы, из стандартной библиотеки:
#include <cstdint> int8_t / uint8_t int16_t / uint16_t int32_t / uint32_t int64_t / uint64_t
На некоторых аппаратных платформах операции с меньшей разрядностью, например uint16_t, могут занимать больше времени чем с аппаратным типом int. Чтобы выбрать тип необходимой разрядности, но не приводящий к снижению быстродействия, необходимо использовать определения std::int_fast#_t. Например, если на некоторой платформе:
то это будет говорить о том, что процессор (микроконтроллер) одинаково быстро работает с типами uint8_t и uint32_t, а вот с числами uint16_t работает медленнее. (Скорость зависит от выборки из памяти и выполнений операций в АЛУ - Арифметико-Логическом Устройстве). Поэтому вместо вычислений с типом uint16_t оптимальнее будет использовать тип uint32_t, который покрывает собой весь диапазон uint16_t.
Есть похожие определения std::int_least8_t, std::int_least16_t, std::int_least32_t которые вернут тип, покрывающий данный диапазон и поддерживающийся в заданной платформе. Например если аппаратура не поддерживает тип uint8_t, а поддерживает только uint16_t и выше, то std::int_least8_t вернет тип uint16_t.
(Источник - radioprog.ru/post - 4.6 – Целочисленные типы фиксированной ширины и size_t).
При работе с беззнаковыми типами можно использовать их переполнение. Но переполнение знаковых типов считается UB, undefined behavior - не гарантируется правильная работа программы.
// OK unsigned int x = 0; // 0x0000_0000, на 64-битной платформе sizeof(x) == 4 unsigned int y = x - 1; // 0xFFFF_FFFF, 4294967295, то есть 2**32 - 1 unsigned int z = y + 1; // 0x0000_0000 // UB, Произведение a * a не помещается в 4 байта, так как оно больше 2^32 unsigned int a = 123456; // на 64-битной платформе sizeof(a) == 4 std::cout << a * a << "\n";
Для получения дробного результата при делении необходимо одну из переменных привести к вещественному типу.
int a = 7, b = 3; int q = a / b; // 2 int r = a % b; // 1 int c = 6, d = 4; double q = static_cast<double>(c) / d; // 1.5
Автоматический вывод типа переменной:
auto x = 42; // int auto pi = 3.14159; // double // НО для строк, выберется тип const char *, а не std::string! auto s = "hello"
Отличная статья, объясняющая зоопарк с разрядностью типов int, long и т.д. - PVS-Studio Blog: Что такое size_t и ptrdiff_t
// содержит стандартные потоки cin, cout, cerr // для языка С они в stdio.h #include <iostream> // Считывание переменных через cin //При наборе данных на клавиатуре значения должны быть разделены: // пробел, \n, \t int64_t a, b; char operation; std::cin >> a >> operation >> b; // Считывание строки без спецсимволов: пробел, \n, \t std::string name; std::cin >> name; // Считывание строки включающей спецсимволы std::string line; std::getline(std::cin, line); for (char symbol : line) { std::cout << symbol << "\t" << static_cast<int>(symbol) << "\n"; // symbol and ascii code } // Считывание пока не закончатся данные // При вводе данных не из файла, а с клавиатуры можно сымитировать конец ввода комбинацией клавиш: // Ctrl+D в Linux и macOS или // Ctrl+Z в Windows. int sum = 0; int x; while (std::cin >> x) { sum += x; } std::cout << sum << "\n"; // или std::string name; while (std::getline(std::cin, name)) { std::cout << "Hello, " << name << "!\n"; }
Подробнее: prog-cpp.ru: Поточный ввод-вывод в C++
std::stod("123.0"); // 123 std::stod("0.123"); // 0.123 std::stod("123.4 with chars"); // 123.4 std::stod("chars 1.2"); // исключение std::invalid_argument string str1 = "123.4 with chars"; string str2 = " 123.4"; size_t ptr1 = -1; size_t ptr2 = -1; auto m1 = std::stod(str1, &ptr1); // m1 = 123.4 ptr1 = 5 auto m2 = std::stod(str2, &ptr2); // m2 = 123.4 ptr2 = 16 std::cout << std::fixed << std::setprecision(1) << m1;
std::this_thread::sleep_for(std::chrono::milliseconds(500));
QVector<uint8_t> table_lin(8); std::iota(table_lin.begin(), table_lin.end(), 0); // table_lin: 0, 1, 2, 3, 4, 5, 6, 7 QVector<uint8_t> table(8); std::fill(table.begin(), table.end(), 3); // table: 3, 3, 3, 3, 3, 3, 3, 3
// CASE 1: int indexOfWidgetRegistered(QWidget *widg) { auto p_match = std::find_if(m_regWidgets.begin(), m_regWidgets.end(), [widg](Widg_RegItem *item){ return (item->widg == widg); } ); if (p_match != m_regWidgets.end()) return p_match - m_regWidgets.begin(); else return -1; } // CASE 2: FormItem *MainWindow::getFormItemBySender(QAction *action) { QList<FormItem*>::iterator match_item = std::find_if(m_formItems.begin(), m_formItems.end(), [action](FormItem *item){ return (item->action == action); } ); if (match_item != m_formItems.end()) return *match_item; else return nullptr; } // CASE 3: const QString c_addrNames[addrCount] = { "CtrlSw1", "CtrlSw2", "CtrlHV", "CtrlOffs1", "CtrlOffs2", "CtrlSw3", ... } QString rd_name = read from somewhere; auto pmatch_name = std::find_if(std::begin(c_addrNames), std::end(c_addrNames), [rd_name](const QString &name){ return name == rd_name; }); if (pmatch_name != std::end(c_addrNames)) { i = pmatch_name - std::begin(c_addrNames); // index ready for next operations }
struct UID_RegItem { Param_UIH *uih; QLabel *lblName; QLabel *lblUnits; bool updNames; UID_RegItem(Param_UIH *_uih, QLabel *_lblName, QLabel *_lblUnits, bool _updNames) : uih{_uih} , lblName{_lblName} , lblUnits{_lblUnits} , updNames{_updNames} {} }; int UI_ParamEventListener::indexOfRegWidget(const std::list<UID_RegItem> &items, const QWidget *widg) { if (widg == nullptr) return -1; const auto p_match = std::find_if(items.begin(), items.end(), [widg](const UID_RegItem &item){ return (item.uih->widget() == widg); } ); if (p_match != items.end()) return std::distance(items.begin(), p_match); else return -1; }
uint32_t m_values[addrCount]; uint32_t fill_value = 0; std::fill(m_values, m_values + addrCount, fill_value);
QVector<float> m_x; QVector<float> m_y; QRectF m_boundingRect; size_t m_pntCount; const auto [minY, maxY] = std::minmax_element(m_y.constBegin(), m_y.constBegin() + m_pntCount); m_boundingRect.setCoords(m_x.constFirst(), *minY, m_x.constLast(), *maxY);
The example removes all (key, value) pairs where the key and the value are the same.
QMutableMapIterator<QString, QString> i(map); while (i.hasNext()) { i.next(); if (i.key() == i.value()) i.remove(); }
Исходный Map:
typedef int32_t uid_t; struct Par_RegItem { // fields ... }; QMap<uid_t, Par_RegItem*> m_regParams;
Неправильный вариант 1 - container-anti-pattern:
for (auto uid : m_regParams.keys()) { Par_RegItem *item = m_regParams[uid]; } Warning: allocating an unneeded temporary container [clazy-container-anti-pattern] Recommended: for (auto i : hash.values()) {} // Iterate the hash directly instead: for (auto i : hash) {}
Неправильный вариант 2 - range-loop-detach:
for (auto hash : m_regParams) { Par_RegItem *item = hash; // hash это и есть //"Par_RegItem *"// } Warning: c++11 range-loop might detach Qt container (QMap) [clazy-range-loop-detach] Recommended: Fix it by marking the container const, or, since Qt 5.7, use qAsConst(): for (auto i : qAsConst(list)) { ... }
Видимо так:
for (auto hash : qAsConst(m_regParams)) { Par_RegItem *item = hash; }
Итератор по Key and Value:
for (auto iter = m_regParams.constBegin(); iter != m_regParams.constEnd(); ++iter) { Par_RegItem *item = iter.value(); uid_t uid = iter.key(); }
Под впечатлением от "Habr: Inline variables".
Константы и переменные объявленные (и определенные) в *.h файле без externt имеют внутреннее связывание. Т.е. эти переменные и константы получают свою копию в каждой единице трансляции. Они дублируются (занимают память) в каждый *.obj файл при компиляции *.cpp в которых подключен данный *.h файл.
При использовании externt к переменной, "ссылка" на эту внешнюю переменную выносится наружу *.obj файла и затем вместо "ссылки" линкер подставляет реальный адрес данной переменной, из той единицы трансляции в которой она определена.
Поэтому при создании констант до стандарта С++17, чтобы избежать дублирования, необходимо было определять константы в *.cpp файле, а объявлять их с externt в *.h файле.
// consts.h namespace NT { namespace KeysFTDI { extern const char * const SerialNum; extern const char * const BuffLenTX; extern const char * const BuffLenRX; extern const char * const TimeoutTx_ms; extern const char * const TimeoutRx_ms; extern const char * const Latency_ms; } }
// consts.cpp namespace NT { namespace KeysFTDI { const char * const SerialNum = "FTDI/SerialNum"; const char * const BuffLenTX = "FTDI/BuffLenTX"; const char * const BuffLenRX = "FTDI/BuffLenRX"; const char * const TimeoutTx_ms = "FTDI/TimeoutTx_ms"; const char * const TimeoutRx_ms = "FTDI/TimeoutRx_ms"; const char * const Latency_ms = "FTDI/Latency_ms"; } } // NT constexpr - здесь не делает связывание внешним! Константы дублируются!
Начиная со стандарт С++17 можно использовать директиву inline для указания внешнего связывания для переменной определенной в *.h файле. Т.е. компилятор сам соберет все inline переменные / константы и расположит их в одной единице трансляции, из которой потом линкер подставит реальные адреса этих переменных/констант при сборке *.obj файлов.
Термин inline теперь разрешает множественное определение одной и той-же сущности для каждой единицы трансляции.
// consts.h namespace NT { namespace KeysFTDI { inline const char * const SerialNum = "FTDI/SerialNum"; inline const char * const BuffLenTX = "FTDI/BuffLenTX"; inline const char * const BuffLenRX = "FTDI/BuffLenRX"; inline const char * const TimeoutTx_ms = "FTDI/TimeoutTx_ms"; inline const char * const TimeoutRx_ms = "FTDI/TimeoutRx_ms"; inline const char * const Latency_ms = "FTDI/Latency_ms"; } }
Со статическими полями классов происходит все аналогично. На примере массива обработчиков некоего класса-парсера, до С++17 необходимо:
// Parser.h <code> class Parser { private: // Тип функции - обработчика typedef bool (*ParseHandler) (uint16_t *rx_data, uint16_t rx_count); // Функции обработчики протокола входных данных static bool parseMess(uint16_t *rx_data, uint16_t rx_count); static bool parseMessEx(uint16_t *rx_data, uint16_t rx_count); static bool parseFunc(uint16_t *rx_data, uint16_t rx_count); static bool parseReadBuff(uint16_t *rx_data, uint16_t rx_count); static bool parseOscData(uint16_t *rx_data, uint16_t rx_count); static bool parseOscReconf(uint16_t *rx_data, uint16_t rx_count); // Массив функций-обработчиков static ParseHandler parseHandlers[PC_ACK_COUNT]; ... }
// Parser.cpp Parser::ParseHandler Parser::parseHandlers[PC_ACK_COUNT] { &Parser::parseMess, &Parser::parseMessEx, &Parser::parseFunc, &Parser::parseReadBuff, &Parser::parseOscData, &Parser::parseOscReconf };
Начиная с С++17 теперь можно так:
class Parser { private: // Тип функции-обработчика typedef bool (*ParseHandler) (uint16_t *rx_data, uint16_t rx_count); // Методы-обработчики протокола входных данных static bool parseMess(uint16_t *rx_data, uint16_t rx_count); static bool parseMessEx(uint16_t *rx_data, uint16_t rx_count); static bool parseFunc(uint16_t *rx_data, uint16_t rx_count); static bool parseReadBuff(uint16_t *rx_data, uint16_t rx_count); static bool parseOscData(uint16_t *rx_data, uint16_t rx_count); static bool parseOscReconf(uint16_t *rx_data, uint16_t rx_count); // Массив методов-обработчиков static inline ParseHandler parseHandlers[PC_ACK_COUNT] { &Parser::parseMess, &Parser::parseMessEx, &Parser::parseFunc, &Parser::parseReadBuff, &Parser::parseOscData, &Parser::parseOscReconf }; ... }
Заменяем С-шный #define на C++ шный inline constexpr:
// example.h #define MY_CONST_1 1 inline constexpr int MY_CONST_2 = 2; // Default choice inline const std::string MY_STR = "2"; // If not a literal type struct A { static constexpr int n = 5; // Default choice; implicitly inline static inline const std::string s = "6"; // If not a literal type }; // example.cpp constexpr int MY_CONST_2 = 2; // Default choice; implicitly static const std::string s4 = "4"; // If not a literal type; implicitly static void f() { static constexpr int n = 7; // Default choice static const std::string s = "8"; // If not a literal type }
Вместо статических методов-обработчиков описаных выше, можно сделать так (спасибо Stackoverflow):
#include <iostream> #include <array> #include <functional> struct Foo { std::array<std::function<void()>, 3> funArray; // Notice the change in signature to void() Foo() { funArray[0] = [&](){ fun1(); }; // Here & is catching this by reference and this lambda will always call fun1 on the current object. funArray[1] = [&](){ fun2(); }; } void fun1() { std::cout << "fun1\n"; } void fun2() { std::cout << "fun2\n"; } std::function<void()> getFunction(int i) { return funArray[i]; } }; int main() { Foo foo; foo.getFunction(0)(); // We get a function returned, so we need to call if by adding one more () at the end auto storedFunction = foo.getFunction(1); // We can also store it storedFunction(); // and call it later }
QSplitter - это Widget который реализует "масштабирование" внутри себя своих child. Поэтому растягиваем QSplitter обычным Layout менеджером на всю форму в конструкторе. Задаем направление QSplitter - вертикальное или горизонтальное.
m_layout = new QVBoxLayout; m_layout->setContentsMargins(0, 0, 0, 0); m_layout->setSpacing(0); setLayout(m_layout); m_splitter = new QSplitter(this); m_splitter->setChildrenCollapsible(false); m_splitter->setOrientation(Qt::Vertical); m_layout->addWidget(m_splitter);
Добавляем формы UiOscFrame в сплиттер по нажатию какой-нибудь кнопки Add обычным образом. Но с удалением формы из сплиттера есть нюансы. Надо вызвать Hide() для формы, чтобы место в сплиттере освободилось. Затем форму можно удалять. Для удобства создаваемые формы я храню в QList<UiOscFrame *> m_frames. Лог показывает, что m_splitter→count() показывает правильное количество виджетов в сплиттере при добавлении и удалении.
// Add Widget: UiOscFrame *osc_frame = new UiOscFrame(this); m_frames.append(osc_frame); m_splitter->addWidget(osc_frame); qDebug() << "Add SplitterChilds: " << m_splitter->count(); // Del Widget UiOscFrame *osc_frame = m_frames.takeAt(m_frames.count() - 1); osc_frame->hide(); delete osc_frame; qDebug() << "Del SplitterChilds: " << m_splitter->count();
std::optional<int32_t> smPosItemLo(AppID app) { if (app > NT_SM_DLL::AppID_LaserExt) return {}; int32_t value = ... return {value}; } auto posItmLo = smPosItemLo(app); if (posItmLo.has_value()) { ui->lbSelPos->setText(QString::number(*posItmLo)); }
static std::pair<int32_t, bool> getMeanRange() { int32_t retMeanRange = ...; bool retIsValid = ...; return {retMeanRange, retIsValid}; } auto [meanRange, rangeValid] = getMeanRange(brd, sm);
int maskToBitInd(uint16_t mask) { int indOf1 = std::countr_zero(mask); if (indOf1 == 16)) // 0 or 16 bits of zeros return -1; else return indOf1; } countr_zero( 00000000 ) = 8 countr_zero( 11111111 ) = 0 countr_zero( 00011100 ) = 2 countr_zero( 00011101 ) = 0
Источник - Habr: Числа, которые должен знать каждый программист
Обращение к кэшу L1 | 0.5 нс |
Ошибка при предсказании условного перехода | 5 нс |
Обращение к кэшу L2 | 7 нс |
Открытие/закрытие мьютекса | 25 нс |
Обращение к главной памяти | 100 нс |
Сжатие 1 Кб быстрым алгоритмом | 3,000 нс |
Пересылка 2Кб по сети со скоростью 1 Гб/с | 20,000 нс |
Чтение 1 Мб последовательно из главной памяти | 250,000 нс |
Передача сообщения туда/обратно в одном дата-центре | 500,000 нс |
Произвольный доступ к жёсткому диску | 10,000,000 нс |
Чтение 1 Мб последовательно с жёсткого диска | 20,000,000 нс |
Передача пакета из Калифорнии в Нидерланды и обратно | 150,000,000 нс |