this
на объект класса, который этот метод вызывает. То есть вызов objectName.foo(100)
компилируется в нечто такое foo(&objectName, 100)
.std::bind
проще использовать лямбду. Если в списке захвата лямбды указать this
или =
, то всеми видимыми методами класса в лямбде можно пользоваться как обычно, https://godbolt.org/z/e9z76vTY7.#include <array>
#include <algorithm>
#include <functional>
#include <iostream>
class Container {
public:
bool isZeroLambda() {
return std::ranges::all_of(std::begin(ar), std::end(ar),
[this](auto x) { return isZeroValue(x); });
}
bool isZeroBind() {
auto pred = std::bind(&Container::isZeroValue, this, std::placeholders::_1);
return std::ranges::all_of(std::begin(ar), std::end(ar), pred);
}
private:
bool isZeroValue(int x) { return x == 0; }
std::array<int, 3> ar{};
};
int main() {
Container c;
std::cout << c.isZeroLambda()
<< '\n' << c.isZeroBind();
return 0;
}
#include <iostream>
#include <future>
#include <atomic>
#include <memory>
#include <string>
class ScanTask {
public:
struct Result {
enum {
Success, Failure, Canceled
} status;
std::string message;
};
ScanTask(const std::string& dir)
: canceled_{ false },
result_{std::async(&ScanTask::scan, this, dir)}
{ }
void cancel() {
canceled_ = true;
}
Result result() {
return result_.get(); // result_.valid() == false
}
Result scan(const std::string& dir) {
struct Finalize {
~Finalize() {
// emit signal scan completed
}
} finalizeSignal;
while (!canceled_ /* && not done */) {
// .. scan subdirectories
/*
if (something failed)
return {Result::Failure, "Failure"};
*/
}
if (canceled_)
return { Result::Canceled, std::string("[Canceled] ") + dir };
return { Result::Success, std::string("[Success] ") + dir };
}
private:
std::atomic<bool> canceled_;
std::future<Result> result_;
};
int main() {
auto localScanTask = std::make_unique<ScanTask>("a");
localScanTask->cancel();
std::cout << localScanTask->result().message << std::endl;
auto sharedScanTask = std::make_unique<ScanTask>("b");
sharedScanTask->cancel();
std::cout << sharedScanTask->result().message << std::endl;
return 0;
}
calculator.divide(1, 2);
. Достаточно собрать эти функции в нашем пространстве имен my_math, например,namespace my_math {
bool operator<(const std::string& st1, const std::string& st2) {
size_t len1 = st1.length();
size_t len2 = st2.length();
if (len1 != len2) return (len1 < len2);
long int i = 0;
// ищем разряд, в котором значения отличаются
while (i < len1 && st1[i] == st2[i])
{
i++;
}
// если разряд найден, то меньше число с меньшей цифрой для положительных и с большей цифрой для отрицательных, иначе числа равны
return (i < len1) && ((st1[i] < st2[i]) );
}
bool les(int lhs, int rhs) {
return lhs <= rhs;
}
}
int main() {
using my_math::operator<; // иначе cout << my_math::operator<(s1, s2) << endl;
std::string s1 = "1", s2 = "2";
cout << (s1 < s2) << endl;
int a = 1, b = 2;
cout << (my_math::les(a, b)) << endl;
return 0;
}
MyClass::~MyClass()
{
if (this->exit_flag_dPManager)
this->exit_flag_dPManager = false;
if (thr_dPManager.joinable())
thr_dPManager.join();
}
MyClass() : exit_flag_dPManager(false) ....
void run() {
while (!exit_flag_dPManager.load()) { ... }
}
~MyClass() {
this->exit_flag_dPManager = true;
if (thr_dPManager.joinable())
thr_dPManager.join();
}
#include <iostream>
#include <string>
using namespace std;
int main()
{
string words;
cin >> words;
cout << words;
return 0;
}
#include <iostream>
using namespace std;
int main() {
cout << "2 << 1 == " << (0b010 << 1); // 0b100
return 0;
}
#include <iostream>
using namespace std;
struct Foo {
Foo& operator<<(int x) {
cout << "Integer: " << x << '\n';
return *this;
}
Foo& operator<<(const char* str) {
cout << "String: " << str << '\n';
return *this;
}
};
int main() {
Foo foo;
foo << 3; // Integer: 3
foo.operator<<("Hello world"); // String: Hello world
return 0;
}
Синонимы: CP1251; ANSI (только в русскоязычной ОС Windows).
#include <fstream>
#include <string>
int main() {
const std::string path = "<путь к файлу>";
std::fstream file(path, std::ios::in);
std::string text;
std::getline(file, text, '\0');
file.close();
text += "[edit]\n";
file.open(path, std::ios::out);
file << text;
return 0;
}
#include <SFML/Graphics.hpp>
#include <unordered_set>
#include <unordered_map>
using namespace std;
void normalize(sf::Vector2i& v) {
int length = sqrt(1ll * v.x * v.x + 1ll * v.y * v.y);
if (length != 0) {
v.x /= length;
v.y /= length;
}
}
class Input {
public:
using Keys = std::unordered_set<sf::Keyboard::Key>;
void registerKeyEvent(sf::Event e) {
if (e.type == sf::Event::KeyPressed)
pressedKeys_.insert(e.key.code);
else if (e.type == sf::Event::KeyReleased)
pressedKeys_.erase(e.key.code);
}
const Keys& pressedKeys() const {
return pressedKeys_;
}
private:
Keys pressedKeys_;
};
class Player {
public:
enum class Control {
moveUp, moveLeft, moveRight, moveDown
};
Player(sf::Vector2i initialPosition, sf::Color color)
: position_{ initialPosition } {
sprite_.setPosition(sf::Vector2f(initialPosition.x, initialPosition.y));
sprite_.setFillColor(color);
sprite_.setRadius(50);
}
Player& map(Control control, sf::Keyboard::Key key) {
keyControls_[key] = control;
return *this;
}
void process(const Input& input) {
velocity_ = {};
for (sf::Keyboard::Key k : input.pressedKeys()) {
if (auto it = keyControls_.find(k); it != keyControls_.end()) {
switch (it->second) {
case Control::moveDown:
velocity_.y = 1;
break;
case Control::moveUp:
velocity_.y = -1;
break;
case Control::moveLeft:
velocity_.x = -1;
break;
case Control::moveRight:
velocity_.x = 1;
break;
default:
break;
}
}
}
normalize(velocity_);
velocity_ *= speed_;
}
void move(sf::Vector2i position) {
position_ = position;
sprite_.setPosition(sf::Vector2f(position.x, position.y));
}
const sf::CircleShape& sprite() const {
return sprite_;
}
sf::Vector2i position() const {
return position_;
}
sf::Vector2i velocity() const {
return velocity_;
}
private:
int speed_ = 300; // pixels / s
sf::Vector2i velocity_;
sf::Vector2i position_;
sf::CircleShape sprite_;
std::unordered_map<sf::Keyboard::Key, Control> keyControls_;
};
class Game {
public:
Game(sf::RenderWindow* gameWindow)
: window_(gameWindow) {
window_->setKeyRepeatEnabled(false);
auto width = window_->getSize().x;
auto height = window_->getSize().y;
Player mrRed(sf::Vector2i(width / 3, height / 3 * 2), sf::Color::Red);
mrRed.map(Player::Control::moveUp, sf::Keyboard::W)
.map(Player::Control::moveDown, sf::Keyboard::S)
.map(Player::Control::moveLeft, sf::Keyboard::A)
.map(Player::Control::moveRight, sf::Keyboard::D);
players_.emplace_back(std::move(mrRed));
Player mrBlue(sf::Vector2i(width / 3 * 2, height / 3 * 2), sf::Color::Blue);
mrBlue.map(Player::Control::moveUp, sf::Keyboard::Up)
.map(Player::Control::moveDown, sf::Keyboard::Down)
.map(Player::Control::moveLeft, sf::Keyboard::Left)
.map(Player::Control::moveRight, sf::Keyboard::Right);
players_.emplace_back(std::move(mrBlue));
Player mrGreen(sf::Vector2i(width / 2, height / 3), sf::Color::Green);
mrGreen.map(Player::Control::moveUp, sf::Keyboard::I)
.map(Player::Control::moveDown, sf::Keyboard::K)
.map(Player::Control::moveLeft, sf::Keyboard::J)
.map(Player::Control::moveRight, sf::Keyboard::L);
players_.emplace_back(std::move(mrGreen));
}
void update() {
processInputEvents();
sf::Time dt = clock_.restart();
processMovement(dt.asMilliseconds());
drawScene();
}
private:
void processInputEvents() {
for (sf::Event e; window_->pollEvent(e); ) {
if (e.type == sf::Event::Closed) {
window_->close();
return;
}
input_.registerKeyEvent(e);
}
for (auto& player : players_)
player.process(input_);
}
void processMovement(sf::Int32 dt) {
for (int id = 0; id < players_.size(); ++id) {
auto& player = players_[id];
sf::Vector2i position = player.position() + sf::Vector2i(
player.velocity().x * 1e-3f * dt,
player.velocity().y * 1e-3f * dt
);
player.move(position);
}
}
void drawScene() {
window_->clear(sf::Color::White);
for (const auto& player : players_)
window_->draw(player.sprite());
window_->display();
}
std::vector<Player> players_;
Input input_;
sf::RenderWindow* window_;
sf::Clock clock_;
};
int main() {
sf::RenderWindow mainWindow(sf::VideoMode(600, 600), "Circles", sf::Style::Close);
mainWindow.setFramerateLimit(120);
Game game(&mainWindow);
while (mainWindow.isOpen())
game.update();
return 0;
}
template<typename T>
struct Outer {
struct Node {
int data;
};
void foo() { node.data; }
Node node;
};
<unknown> Outer<T>::Node::data
, где unknown - тип, который не удалось вывести.Note: You should use the native driver, if it is available, instead of the ODBC driver. ODBC support can be used as a fallback for compliant databases if no native driver is available.QODBC for Open Database Connectivity (ODBC)
setDatabaseName
указывается не имя базы данных, а datasource name или данные подключения (пример по ссылке выше).When connecting to an ODBC datasource, you should pass the name of the ODBC datasource to the QSqlDatabase::setDatabaseName() function, rather than the actual database name.
int
). Чтобы начать жизнь объекта, его нужно разместить в памяти с соответствующим типу выравниванием. Если выравнивание, равное степени двойки, будет меньше требуемого для типа, то приведение с помощью static_cast
типа указателя в выделенной памяти (void*, char*, unsigned char*
) к указателю на тип объекта создает неопределенное поведение. Значит, чтобы безопасно использовать объект, размещенный в выделенной памяти, нужно убедиться, что он размещен (с помощью placement new) по выровненному адресу.new unsigned char[size]
. Будет ли буфер иметь достаточное выравнивание, чтобы в начале его разместить BUFFER::a
типа int
? - Да, если мы зарезервируем сразу достаточно памяти,
In addition, if the new-expression is used to allocate an array of char, unsigned char, or std::byte (since C++17), it may request additional memory from the allocation function if necessary to guarantee correct alignment of objects of all types no larger than the requested array size, if one is later placed into the allocated array.
new, new[]
автоматически определяют размер выделяемой памяти и передают его низкоуровневым функциям operator new, operator new[]
соответственно. Память, выделенная new
, освобождается вызовом delete
, для new[]
- delete[]
. Поведение delete
похоже на поведение new
выше.#include <cstddef>
#include <iostream>
#include <cstring>
class Buffer {
public:
using preamble_type = int;
static constexpr std::size_t preamble_size = sizeof(preamble_type);
Buffer(const preamble_type& value, std::size_t bufferSize) {
buffer_ = new unsigned char[preamble_size + bufferSize];
::new(buffer_) preamble_type(value);
}
Buffer(const Buffer& other) = delete;
Buffer& operator=(const Buffer& rhs) = delete;
Buffer(Buffer&& other) noexcept
: buffer_(other.buffer_) {
other.buffer_ = nullptr;
}
Buffer& operator=(Buffer&& rhs) noexcept {
std::swap(buffer_, rhs.buffer_);
return *this;
}
virtual ~Buffer() {
destroy();
}
preamble_type& preamble() noexcept {
return const_cast<preamble_type&>(
const_cast<const Buffer *>(this)->preamble()
);
}
const preamble_type& preamble() const noexcept {
return *preambleData();
}
unsigned char* data() const noexcept {
return buffer_ + preamble_size;
}
void resize(std::size_t size) {
if (buffer_ != nullptr) {
auto newBuffer = new unsigned char[preamble_size + size];
::new(newBuffer) preamble_type(std::move(preamble()));
destroy();
buffer_ = newBuffer;
}
}
private:
preamble_type* preambleData() const noexcept {
// return std::launder(reinterpret_cast<preamble_type*>(buffer_)); c++17
return reinterpret_cast<preamble_type*>(buffer_);
}
void destroy() noexcept {
preambleData()->~preamble_type();
delete[] buffer_;
}
unsigned char* buffer_ = nullptr;
};
int main()
{
using std::cout;
using std::endl;
const std::size_t bufferSize = 100;
Buffer b(100, bufferSize);
const char hello[] = "hello world!";
memcpy(b.data(), hello, sizeof(hello));
auto c = std::move(b);
cout << c.preamble() << ' ' << c.data() << endl;
b = Buffer(5, 20);
memcpy(b.data(), hello, sizeof(hello));
cout << b.preamble() << ' ' << b.data() << endl;
return 0;
}
preamble_type
, если считать, что первое поле всегда будет int
. С другой стороны, код можно сделать обобщенным с минимальными изменениями. Например,template<typename PreambleT>
class Buffer {
public:
using preamble_type = PreambleT;
//...
};
class Buffer
, по-моему, скорее плохой, потому что только имитирует на C++ код, написанный на C. Так, мы не храним в классе размер буфера, поэтому нельзя определить операторы копирования. Детали реализации класса "утекают" в пользовательский код.resize
. Даже если бы resize
давал нам новый буфер, значение preamble
потеряно. Если бы мы сохраняли preamble
, тогда в конструкторе перемещения пришлось бы выделять память под новый буфер, но тогда этот конструктор уже не будет noexcept
- плохо. Придется запретить перемещение?p = p2
мы выполняем неявное преобразование встроенных типов, для которых в стандарте не описаны правила преобразования. То есть такое преобразование не гарантировано, но может быть реализовано компилятором, например#include <type_traits>
using namespace std;
int main() {
static_assert(std::is_convertible<int(*)[2], int(*)[]>::value, "Non-convertible");
return 0;
}
Можно ли создать пустую структуру, а потом её заполнить внутри функции?
class Config
. Если нам нужны отдельные объекты вроде struct Date
, можем реализовать фабричный метод, который будет их создавать, как в следующем примере.#include <iostream>
#include <string>
#include <unordered_map>
#include <fstream>
class AppConfig {
public:
static AppConfig make(const std::string& path) {
auto attributes = readAttributes(path);
if (attributes.empty()) {
// signal error - no attributes, no config
return {};
}
return parse(attributes);
}
std::string title = "";
bool fullscreen = false;
bool windowMode = false;
int width = 0;
int height = 0;
int renderApi = 0;
private:
static std::unordered_map<std::string, std::string>
readAttributes(const std::string& path) {
std::unordered_map<std::string, std::string> attributes;
std::ifstream configFile(path);
if (!configFile) {
// signal error - failed to open file
return {};
}
std::string attribute, value;
while (configFile >> attribute >> value) // attribute value
attributes[attribute] = value;
return attributes;
}
static AppConfig parse(std::unordered_map<std::string, std::string>& attributes) {
static constexpr auto trueValue = "true";
AppConfig config;
config.title = attributes["title"];
config.fullscreen = attributes["fullscreen"] == trueValue;
config.windowMode = attributes["window_mode"] == trueValue;
config.width = std::stoi(attributes["width"]);
config.height = std::stoi(attributes["height"]);
config.renderApi = std::stoi(attributes["render_API"]);
return config;
}
};
class Window {
public:
static bool create_window(...) { return true; }
};
bool initialize() {
auto config = AppConfig::make("settings.txt");
if (!Window::create_window(config.title, config.fullscreen, config.windowMode,
config.width, config.height))
return false;
//...
return true;
}
#include <unordered_map>
#include <string>
#include <any>
class AppConfig {
public:
explicit AppConfig(const std::string& path) { /* read values */ }
std::any operator[](const std::string& attribute) { return attributes[attribute]; }
private:
std::unordered_map<std::string, std::any> attributes;
};
class Window {
public:
static bool create_window(...) { return true; }
};
bool initialize() {
auto config = AppConfig("settings.txt");
if (!Window::create_window(
std::any_cast<std::string>(config["title"]),
std::any_cast<bool>(config["fullscreen"]),
std::any_cast<bool>(config["window_mode"])
/* ... */)) {
return false;
}
return true;
}
vector<T>::data()
.#include <iostream>
using namespace std;
using esp_spp_cb_event_t = int;
using esp_spp_cb_param_t = void;
void btCallback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
cout << "btCallback" << endl;
}
class MyBt {
public:
typedef void (*callback_type)(esp_spp_cb_event_t, esp_spp_cb_param_t *);
void register_callback(callback_type cb) {
cb(0, nullptr);
}
};
int main()
{
MyBt bt;
bt.register_callback(btCallback);
return 0;
}
~Parent() = default
(ничего не делать) или чистым виртуальным virtual ~Parent() = 0
(не определен). Только во втором случае невозможно создавать объекты типа "Parent". dumb_array & operator=( dumb_array temp)
, в котором temp инициализируется посредством метода dumb_array (dumb_array&& other)
, который перед выводом на экран сообщения operator=( dumb_array tmp) вызывает ещё один конструктор по умолчанию.#include <iostream>
using namespace std;
struct Foo {
Foo() { cout << "default ctor\n"; }
Foo(Foo&& arg) { cout << "move ctor\n"; }
};
void foo(Foo arg) {
cout << "foo\n";
}
int main () {
foo(Foo());
return 0;
}
In the initialization of an object, when the source object is a nameless temporary and is of the same class type (ignoring cv-qualification) as the target object.
Mandatory copy/move elision in Visual Studio
The C++ standard requires copy or move elision when the returned value is initialized as part of the return statement (such as when a function with return type Foo returns return Foo()). The Microsoft Visual C++ compiler always performs copy and move elision for return statements where it is required to do so, regardless of the flags passed to the compiler. This behavior is unchanged.
This is an optimization: even when it takes place and the copy/move (since C++11) constructor is not called, it still must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed
operator+(Car& c1, Car& c2)
функция GetCar
должна возвращать Car&
, а не Car
или const Car&
(только не возвращайте ссылку на переменную, объявленную внутри функции).operator+(Car& c1, Car& c2)
на operator+(const Car& c1, const Car& c2)
.