Грокаем C++
@grokaemcpp
16 12 1.3K
std::string makeText()
{
std::string s = "Hello World!";
return s;
}
int main()
{
std::string t = makeText();
std::cout << t << endl;
}s, затем ещё два — для возврата по значению: сначала копирование-инициализация временного объекта из s, затем копирование-инициализация t из временного объекта. Каждое копирование должно триггерить аллокацию нового буфера.std::string не требуется выделять память в куче (или любую другую память, о которой знает его аллокатор). Требуется лишь, чтобы он мог тем или иным способом хранить строку символов произвольной длины. Действительно, в общем случае это подразумевает выделение памяти в какой-то момент. Но наш случай не общий вообще-то. Текст "Hello World!" довольно короткий. Это 12 символов; 13, если считать завершающий нулевой символ. Типичная реализация std::string требует трёх указателей для полноценного функционирования (примерно также как мы рассказали тут для std::vector). Если на 64-разрядной машине размер указателя равен 8 символам, то наш текст занимает меньше, чем два указателя. Текст достаточно мал, чтобы поместиться внутрь объекта, память для которого выделена на стеке. Это идеальный кандидат для оптимизации малого буфера (small buffer optimization, SBO).
std::string makeText()
{
std::string s = "Hello World!";
return s;
}
int main()
{
std::string t = makeText();
std::cout << t << endl;
}
kernel.pid_max, который определяет максимально возможный идентификатор задачи. Исторически значение по умолчанию было 65535, но в современных ядрах оно может быть значительно выше — до нескольких миллионов.nproc(number of processes). Его можно посмотреть командой ulimit -u:/etc/security/limits.conf или /etc/security/limits.d/.
cat /proc/sys/kernel/threads-max.
void foo() {
std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::cout << vec.empty();
}void foo() {
using ArrType = std::array<int, 5>;
auto * ptr = new ArrType{0, 1, 2, 3, 4};
ptr->~ArrType();
}struct A {
int i;
char c;
};void foo() {
A obj;
}
foo();void foo() {
using ArrType = std::array<int, 5>; // just for explicit destructor call
auto * ptr = new ArrType{0, 1, 2, 3, 4};
ptr->~ArrType();
}void foo() {
std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::cout << vec.empty();
}vec располагается на стеке, поэтому для этого объекта автоматически выделяется и освобождается память, просто за счет увеличения и уменьшения указателя на стек.vec. Память под объект - это стековая память и ей управляет рантайм.
struct Example {
~Example() {}
};
Example a;
Example b;
a = std::move(b);
struct Bad {
Bad() : p{new int{5}} {}
Bad(Bad&& other) {
p = other.p;
other.p = nullptr;
}
int * p;
};
{
Bad b;
} // memory leakstruct Example {
Example() = default;
Example(Example&&) {}
};
Example a;
Example b = a; // Error: copy ctor is implicitly declared as deleted
Example c;
c = a; // Error: copy assign operator is implicitly declared as deletedstruct Bad {
Bad() : p{new int{5}} {}
~Bad() {delete p;}
int * p;
};
{
Bad b;
Bad b1 = b;
} // double freestruct Example {
~Example() {}
// or Example(Example&& other) {}
// or Example& operator=(const Example&) {}
// or Example& operator=(Example&&) {}
};
Example a;
Example b;
a = std::move(b); // Error: no move assign
std::vector<int> v = {1, 2, 3};
// нарушение контракта: 5 >= 3
int x = v[5];
v.pop_back();
v.pop_back();
v.pop_back();
// нарушение контракта: нельзя убрать элемент из пустого вектора
v.pop_back();
std::string_view sv("hello");
// нарушение контракта: 10 >= 5
char c = sv[10];
// нарушение контракта: 10 > 5
sv.remove_prefix(10);
std::optional<int> opt;
// нарушение контракта: нет реального объекта
int x = *opt;
int data[10];
// нарушение контракта: разное число элементов
// в шаблонном параметре и в аргументе конструктора
std::span<int, 5> sp(data, 3);
// нарушение контракта: 10 > size()
sp.first<10>();
template<typename T>
class Vector {
...
T * begin;
size_t size;
size_t capacity;
};char *, но смысл один: есть указатель на начало и 2 размера: текущее количество элементов и максимально возможное без переаллокаций.template<typename T>
class Vector {
...
T * begin;
T * end;
T * end_of_storage;
};
class rule_of_zero
{
std::string cppstring;
public:
rule_of_zero(const std::string& arg) : cppstring(arg) {}
};
// std::string will manage resourceclass Base {
public:
virtual ~Base() = default;
Base(const Base&) = delete;
Base& operator=(const Base&) = delete;
Base(Base&&) = delete;
Base& operator=(Base&&) = delete;
// ... other constructors and functions ...
};
class PString {
std::string* ptr;
public:
PString (const std::string& str) : ptr(new std::string(str)) {}
};class PString {
std::string* ptr;
public:
PString (const std::string& str) : ptr(new std::string(str)) {}
~PString () {delete ptr;}
}; class PString {
std::string* ptr_ = nullptr;
public:
PString (const std::string& str) : ptr_(new std::string(str)) {}
~PString () {delete ptr_;}
PString (const PString& other) : ptr_(new std::string(*other.ptr_)) {}
PString (PString&& other) noexcept : ptr_(other.ptr_) {ptr_ = nullptr;}
PString& operator=(const PString& other) {
if (this != &other) {
delete ptr_; // free existing resource
ptr_ = new std::string(*other.ptr_); // deep copy
}
return *this;
}
PString& operator=(PString&& x) noexcept {
if (this != &x) {
delete ptr_; // free existing resource
ptr_ = x.ptr_; // move resource and then just like in move constructor
x.ptr_ = nullptr;
}
return *this;
}
};
using File = std::unique_ptr<FILE, decltype(&std::fclose)>;
struct Foo {
std::vector<int> numbers;
File file;
// no explicit destructor ~Foo() {}
};
void foo() {
Foo obj = {{1, 2, 3, 4, 5, 6, 7, 8, 9}, {std::fopen("example.txt", "r"), &std::fclose}};
} // destroy obj herestruct Foo {
std::vector<int> numbers;
// no explicit copy/move constructors
// Foo(const Foo& other) {...}
// Foo(Foo&& other) {...}
};
void foo() {
Foo obj = {{1, 2, 3, 4, 5, 6, 7, 8, 9}};
Foo copied_in_obj = obj; // copy obj
Foo moved_in_obj = std::move(obj); // move from objFoo a, b;
a = b; // copy assign
a = std::move(b); // move assign
Foo a1 = b; // THIS IS NOT AN ASSIGNstruct Foo {
std::vector<int> numbers;
// no explicit copy/move constructors
// Foo& operator=(const Foo& other) {...}
// Foo& operator=(Foo&& other) {...}
};
void foo() {
Foo forward = {{1, 2, 3, 4, 5, 6, 7, 8, 9}};
Foo backward = {{9, 8, 7, 6, 5, 4, 3, 2, 1}};
Foo obj; // created obj here
obj = forward; // copy assign forward to obj
obj = std::move(backward); // move assign backward to obj
thread_create().CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_SIGHAND | CLONE_THREAD и получите процесс, который по своей сути POSIX поток, который шарит эти ресурсы со своей родительской задачей. Такие процессы называют легковесные процессы или LWP.
class ParametrizedConstructor {
int total;
public:
ParametrizedConstructor(int initial_value) : total(initial_value) { };
void accumulate (int x) { total += x; };
};
Example2 ex (100); // ok
Example2 ex1; // error: no default constructorstruct UserCopyConstructor
{
UserCopyConstructor(const UserCopyConstructor&) {}
// UserCopyConstructor::UserCopyConstructor() is implicitly defined as deleted
};
struct UserMoveConstructor
{
UserMoveConstructor(UserMoveConstructor&&) = default;
// UserMoveConstructor::UserMoveConstructor() is implicitly defined as deleted
};struct HasReference {
int& ref; // error: reference needs to be initialized
};struct ConstTrivial {
const int x; // int has a trivial default constructor, so cannot default construct an object
};struct ConstNonTrivial {
const std::string s; // std::string has non-trivial constructor
};
ConstNonTrivial cnt; // OK, s is just empty string= delete) конструктор по умолчаниюstruct NoDefault {
private:
NoDefault() {} // private constructor
};
struct Derived : NoDefault {
// Derived() is implicitly deleted
};
struct DeletedDefault {
DeletedDefault() = delete; // explicilty deleted
};
struct HasDeleted {
DeletedDefault dd;
};
HasDeleted hd; // ERROR! dd has explicilty deleted default constructor
struct Foo {
int i;
double d;
char c;
};struct Foo {
// Foo() = default; так тоже можно
int i;
std::string s;
std::vector<int> v;
};
Foo f = {42, "Hello World", {1, 2, 3}};Foo f = {.i = 42, .s = "Hello World", .v = {1, 2, 3}};struct Foo {
Foo() {};
int i;
std::string s;
std::vector<int> v;
};
Foo f = {42, "Hello World", {1, 2, 3}}; // ERROR