Управление ресурсами
Физические ресурсы
Виртуальные ресурсы
C++ storage duration categories
Объекты с автоматической продолжительностью хранения
Специальные методы классов
Специальные методы классов
Общая схема работы с ресурсами
Захват ресурса без освобождения
“Ручное” управление ресурсами
Stack unwinding
Использование RAII (guards)
std::auto_ptr
std::auto_ptr
std::unique_ptr
std::unique_ptr
std::unique_ptr
std::make_unique
std::shared_ptr
std::shared_ptr
Внутреннее устройство shared_ptr
Пример использования weak_ptr
Передача smart pointers как аргумента
Передача smart pointers как аргумента
Контейнеры
Контейнеры
Exception safety
Another guards
536.71K
Category: programmingprogramming

Управление ресурсами

1. Управление ресурсами

2.

Ресурс – это физический или виртуальный компонент
компьютерной системы, обладающий свойством
ограниченности.
(©Wikipedia)

3. Физические ресурсы

Процессор
Оперативная память
Жесткий диск
Сетевой адаптер
Видеокарта
Звуковая карта
Устройства ввода/вывода (клавиатура, мышь, принтер, сканер и т.п.)

4. Виртуальные ресурсы

Виртуальная память
Файлы
Сокеты
Объекты синхронизации (мьютекс, критическая секция, семафор и т.п.)
Объекты межпроцессного взаимодействия (pipes, mapped files, mailslots и
т.п.)
Аттрибуты безопасности, дескрипторы объектов ОС и т.п.

5. C++ storage duration categories

1.
Static – глобальные переменные, статические локальные переменные, статические
члены классов. Создаются при запуске приложения и разрушаются при выходе.
2.
Thread storage (C++ 11) – переменные, доступные только в потоке (обозначаются
ключевым словом thread_local). Создаются при старте потока, разрушаются при
завершении его работы.
3.
Automatic – локальные переменные. Разрушаются на выходе из блока, в котором они
созданы.
4.
Dynamic – создаются при помощи операторов new и разрушаются при помощи
операторов delete.

6. Объекты с автоматической продолжительностью хранения

Преимущества:
1. Быстрое выделение памяти.
2. Объекты на стеке автоматически разрушаются при выходе из области видимости.
Недостатки:
1. Размер стека ограничен (по умолчанию в Visual C++ 1Mb для каждого потока).
2. Нужно знать размер объекта на этапе компиляции.
3. Стеком сложнее управлять.

7. Специальные методы классов

1. Версии конструкторов копирования и операторов присваивания, генерируемые
компилятором, осуществляют “shallow copy” членов. Необходимо переопределять их, если
такое поведение неверно.
class Test {
int* m_data;
Test(const Test& other) {
m_data = new int(*other.m_data);
}
};

8. Специальные методы классов

2. Проверяйте в операторе присваивание случай самоприсваивания:
Test& operator=(const Test& other) {
if (this != &other) {
delete m_data;
m_data = new int(*other.m_data);
}
return *this;
}

9. Общая схема работы с ресурсами

1. Захватить ресурс (open, create, allocate, attach)
2. Использовать ресурс
3. Освободить ресурс (close, free, delete, detach)
Внимание:
1. Если операция захвата ресурса возвращает статус
успешности, всегда проверяйте его.
2. Используйте правильную операцию для освобождения
ресурса

10. Захват ресурса без освобождения

class Singleton
{
static Singleton& get()
{
static Singleton instance;
return instance;
}
};
class Log {
void Flush();
~Log() { Flush() };
vector<string> buffer;
};
int main() {
Log *log = new Log();
...
return 0;
}

11. “Ручное” управление ресурсами

void f() {
Test *test = new Test;
DoSomething(test);
delete test;
}
CRITICAL_SECTION cs;
void do_work()
{
::EnterCriticalSection(&cs);
f();
::LeaveCriticalSection(&cs);
}
void f()
{
throw runtime_error(“err”);
}
bool f() {
Test *tst = new Test;

// Fixed issue #12
if (!CheckSome()) {
return false;
}

test->Action();
delete test;
return true;
}

12. Stack unwinding

void func()
{
string s1{ "test" };
vector<int> v1{ 1, 4, 5 };
throw runtime_error("");
}
class Test {
public:
~Test()
{
throw std::runtime_error("");
// DON’T DO IT!!!
}
};
void f() {
std::string s1{ "str" };
Test test;
throw std::runtime_error("");
}

13. Использование RAII (guards)

class SocketGuard {
public:
SocketGuard(SOCKET sock_) :
sock_ (sock) {}
operator SOCKET()
{
return sock_;
}
~ SocketGuard()
{
::closesocket(sock_);
}
private:
SOCKET sock_;
};
void f()
{
SOCKET sock = ::socket(…);
if (sock != INVALID_SOCKET)
{
SocketGuard guard(sock);
::connect(guard, …);
}
}

14. std::auto_ptr

class Test
{
};
void f()
{
std::auto_ptr<Test> p(new Test);
...
}
void f()
{
std::auto_ptr<Test> p1(new Test);
...
std::auto_ptr<Test> p2 = p1;
}
// Unsafe, don't do it!!!
std::vector<std::auto_ptr<int>> vec;

15. std::auto_ptr

Недостатки:
1. Не может использоваться в контейнерах STL.
2. Не может быть использован с new[].
3. Неочевидная передача владения (при копировании и передаче в
качестве параметра функции по значению).
4. Нельзя использовать с другими типами ресурсов, отличными от памяти.

16. std::unique_ptr

template<typename T, typename D =
default_delete<T>>
class unique_ptr
{
typedef ... pointer;
typedef T element_type;
typedef D deleter_type;
};
// Specialization for arrays
template<typename T[], typename D>
class unique_ptr
{
typedef ... pointer;
typedef T element_type;
typedef D deleter_type;
};

17. std::unique_ptr

unique_ptr<int> p1{ new int(10) };
//Error: assignment operator is deleted
unique_ptr<int> p2 = p1;
// Correct
unique_ptr<int> p2 = std::move(p1);
// Array
unique_ptr<int[]> p{ new int[10] };
// In container
vector<unique_ptr<int>> vec;

18. std::unique_ptr

void del(void* data){
VirtualFree(data, 0, MEM_RELEASE);
}
void *pData = VirtualAlloc(
nullptr,
0x4000,
MEM_COMMIT,
PAGE_READWRITE);
unique_ptr<void,decltype(&del)> p(
pData, del);
auto del = [](void *data)
{
VirtualFree(data, 0, MEM_RELEASE);
};
unique_ptr<void,decltype(del)> p(
pData, del);

19. std::make_unique

unique_ptr<int> p1{ new int(100) };
// The same
unique_ptr<int> p2{ make_unique<int>(100)};
void f(unique_ptr<SomeClass> p1,
bool);
bool f2(); // Can throw
// Exception safe
f(make_unique<SomeClass>(), f2());
// Take ownership and can throw
void f(SomeClass* p1);
{
// Not exception safe
f(unique_ptr<SomeClass>(new
SomeClass), f2());
}
unique_ptr<SomeClass> t{ new
SomeClass };
f(t.get());
t.release();

20. std::shared_ptr

std::shared_ptr<SomeClass> p1{new SomeClass };
std::shared_ptr<SomeClass> p2 = p1;
std::shared_ptr<int> p{ new int[10] }; // Compiles, but is an error
std::shared_ptr<int> p{ new int[10], std::default_delete<int[]>() };
void thread_func(shared_ptr<vector<int>>& vec) {
...
vec->push_back(5); // Not thread safe
...
}

21. std::shared_ptr

void del(void* data){
VirtualFree(data, 0, MEM_RELEASE);
}
void *pData = VirtualAlloc(
nullptr,
0x4000,
MEM_COMMIT,
PAGE_READWRITE);
shared_ptr<void> p(pData, del);

22. Внутреннее устройство shared_ptr

23. Пример использования weak_ptr

class ObjectLoader {
map<int, std::weak_ptr<Object>> cache_;
shared_ptr<Object> LoadObject(int id) {
std::shared_ptr<Object> object;
std::weak_ptr<Object> cached = cache_.find(id);
if (cached != cache_.end()) {
object = cached.lock();
if (object != nullptr) return object;
}
object = LoadFromDatabase(id);
cache[id] = object;
}
};

24. Передача smart pointers как аргумента

void f(std::unique_ptr<int> p);
// Possible but meaningless
void f(const unique_ptr<Tst>& p);
f(make_unique<int>(10));
// If parameter cannot be null
void f(const Tst& p);
void f(std::unique_ptr<int>& p)
{
p.reset(new int(100));
}
std::unique_ptr<int> p(new int(10));
f(p);
// If parameter can be null
void f(const Tst* p);

25. Передача smart pointers как аргумента

void f(std::shared_ptr<int> arg);
{
std::shared_ptr<int> p(new int(10);
f(p);
}

26. Контейнеры

void FillBuffer(
void* pBuffer,
size_t bufferSize);
// version with smart pointer
std::unique_ptr<uint8_t[]> buffer{
new uint8_t[1024] };
FillBuffer(buffer.get(), 1024);
// version with raw ponter
uint8_t *buf = new uint8_t[size];
// version with vector
FillBuffer(buf, size);
vector<uint8_t> buffer(size);
delete[] buf;
FillBuffer(&buffer[0], size);

27. Контейнеры

// First DLL
// Second DLL
vector<Message> messages;
void FillMessages(vector<Message>&
messages)
FillMessages(messages);
{
for (int i = 0; i < MsgCount(); ++i)
{
messages.push_back(GetMsg(i));
}
}

28. Exception safety

1. Базовая гарантия – контейнер при возникновении исключения остается в согласованном
состоянии; нет утечек ресурсов (пример – вставка диапазона элементов).
2. Строгая гарантия – либо операция заканчивается успешно, либо она не выполняется
вообще (пример vector::push_back).
3. No-throw (nofail) гарантия – операция всегда заканчивается успешно (пример std::swap).
Гарантия предоставляется, если код, предоставляемый пользователем, не оставляет
элементы контейнера в недопустимом состоянии; не приводит к утечкам ресурсов и
деструкторы не кидают исключений.

29. Another guards

1. Часы и таймеры STL.
2. Потоки STL (std::thread, std::mutex, std::lock, etc.).
3. Потоки ввода вывода STL (input/output streams).
4. «Стражи» в ATL (CComPtr, CComSafeArray, СComBSTR, CComVariant)
5. «Стражи» в boost (scoped_ptr, scoped_array, share_array)
English     Русский Rules