Страницы

Поиск по вопросам

воскресенье, 29 декабря 2019 г.

Подсчет ссылок в C++

#cpp #память #counter


Как реализовать подсчет ссылок при создании собственного класса C++? 

То есть нужен счетчик, который хранил бы количество ссылок, ссылающихся на объект.
Если этот счетчик равен нулю, то объект необходимо удалить. Хочется разобраться в этом,
но не имею ни малейшей мысли как это реализовать.
    


Ответы

Ответ 1



Простейший вариант - примерно так (это набросок, не более того): class CountedPtr { ... Type1 p1; // Данные Type2 p2; // Данные .... int * cnt; // Счетчик В конструкторе - помимо прочего - cnt = new int(1); В копирующем конструкторе - cnt копируется и увеличивается на 1. Соответственно обрабатывается присваивание (проще всего его реализовать через идиому обмена с копией). В деструкторе - что-то типа if (--*cnt == 0) { delete ptr; delete cnt; } Для начала (и понимания азов) хватит?

Ответ 2



Все уже реализовано: используйте std::shared_ptr.

Ответ 3



Вот у Страуструпа есть подсчет ссылок при реализации класса String. // String.h #include #include #define DEBUG #ifndef DEFINE_STRING #define DEFINE_STRING class String { struct Srep { char* s; int n; int sz; Srep(int, const char*); ~Srep() { delete[] s; } Srep* GetOwnCopy(); void Assign(int, const char*); private: Srep(const Srep&); Srep& operator=(const Srep&); }; class Cref { public: inline Cref(String&, int); inline Cref(const Cref&); Cref(); inline operator char() const; inline void operator=(char); private: String& s; int i; }; public: class Range { }; inline String(); inline String(const char*); String(const String&); ~String(); String& operator=(const char*); String& operator=(const String&); inline void Check(int) const; inline char Read(int) const; inlinevoid Write(int, char); inline Cref operator[](int); inline char operator[](int) const; inline int Size() const; private: Srep* rep; }; inline String::String() : rep(new Srep(0, "") ) { #ifdef DEBUG std::cout << "inline String::String()" << std::endl; #endif } inline String::String(const char* p) : rep(new Srep(strlen(p), p) ) { #ifdef DEBUG std::cout << "inline String::String(const char* p)" << std::endl; #endif } inline void String::Check(int i) const { #ifdef DEBUG std::cout << "inline void String::Check(int i) const" << std::endl; #endif if (i < 0 || rep->sz <= i) throw Range(); } inline char String::Read(int i) const { #ifdef DEBUG std::cout << "inline char String::Read(int i) const" << std::endl; #endif return rep->s[i]; } inline void String::Write(int i, char c) { #ifdef DEBUG std::cout << "inline void String::Write(int i, char c)" << std::endl; #endif rep = rep->GetOwnCopy(); rep->s[i] = c; } inline String::Cref String::operator[](int i) { #ifdef DEBUG std::cout << "inline String::Cref String::operator[](int i)" << std::endl; #endif Check(i); return Cref(*this, i); } inline char String::operator[](int i) const { #ifdef DEBUG std::cout << "inline char String::operator[](int i) const" << std::endl; #endif Check(i); return rep->s[i]; } inline int String::Size() const { #ifdef DEBUG std::cout << "inline int String::Size() const" << std::endl; #endif return rep->sz; } inline String::Cref::Cref(String& ss, int ii) : s(ss), i(ii) { #ifdef DEBUG std::cout << "inline String::Cref::Cref(String& ss, int ii)" << std::endl; #endif } inline String::Cref::Cref(const Cref& r) : s(r.s), i(r.i) { #ifdef DEBUG std::cout << "inline String::Cref::Cref(const Cref& r)" << std::endl; #endif } inline String::Cref::operator char() const { #ifdef DEBUG std::cout << "inline String::Cref::operator char() const" << std::endl; #endif s.Check(i); return s.Read(i); } inline void String::Cref::operator=(char c) { #ifdef DEBUG std::cout << "inline void String::Cref::operator=(char c)" << std::endl; #endif s.Write(i, c); } #endif // String.cpp #include "String.h" String::String(const String& r) { #ifdef DEBUG std::cout << "String::String(const String& r)" << std::endl; #endif r.rep->n++; rep = r.rep; } String::~String() { #ifdef DEBUG std::cout << "String::~String()" << std::endl; #endif if (--rep->n == 0) delete rep; } String& String::operator=(const char* p) { #ifdef DEBUG std::cout << "String& String::operator=(const char* p)" << std::endl; #endif if (rep->n == 1) rep->Assign(strlen(p), p); else { rep->n--; rep = new Srep(strlen(p), p); } return *this; } String& String::operator=(const String& r) { #ifdef DEBUG std::cout << "String& String::operator=(const String& r)" << std::endl; #endif r.rep->n++; if (--rep->n == 0) delete rep; rep = r.rep; return *this; } String::Srep::Srep(int nsz, const char* p) { #ifdef DEBUG std::cout << "String::Srep::Srep(int nsz, const char* p)" << std::endl; #endif n = 1; sz = nsz; s = new char[sz + 1]; strcpy(s, p); } String::Srep* String::Srep::GetOwnCopy() { #ifdef DEBUG std::cout << "String::Srep* String::Srep::GetOwnCopy()" << std::endl; #endif if (n == 1) return this; n--; return new Srep(sz, s); } void String::Srep::Assign(int nsz, const char* p) { #ifdef DEBUG std::cout << "void String::Srep::Assign(int nsz, const char* p)" << std::endl; #endif if (nsz != sz) { delete[] s; sz = nsz; s = new char[sz + 1]; } strcpy(s, p); }

Ответ 4



Я тоже оставлю здесь пример своей псевдореализации умного указателя, так как вопрос все-таки больше связан как реализовать самостоятельно, а не воспользоваться готовыми классами(хотя лучше пользоваться стандартными классами). Это всего лишь учебный пример, потому что Когда-то я также интересовался, как устроены умные указатели. template class my_smart_ptr { private: int* count; T* obj; public: my_smart_ptr(T* v) : obj(v) , count(new int) { *count = 1; std::cout << "my_smart_ptr: increment counter" << std::endl; } ~my_smart_ptr() { --*count; std::cout << "my_smart_ptr: decrement counter" << std::endl; if (*count == 0) { delete obj; obj = nullptr; delete count; count = nullptr; std::cout << "my_smart_ptr deleted the object. The count equals 0" << std::endl; } } T* operator -> () { return obj; } my_smart_ptr(const my_smart_ptr& other) { count = other.count; obj = other.obj; ++*count; std::cout << "my_smart_ptr: increment counter" << std::endl; } my_smart_ptr operator = (const my_smart_ptr& other) { my_smart_ptr t(other); return t; } }; class example { private: int _a; public: example(int a) : _a(a) { std::cout << "example: Call Constructor example" << std::endl; } ~example() { std::cout << "example: Call Destructor example: a=" << _a << std::endl; } void setA(int a) { _a = a; } }; my_smart_ptr foo() { auto t = my_smart_ptr(new example(10)); int a = 10 + 5; a += 5; return t; } void foo2(my_smart_ptr ex) { ex->setA(5); } void foo3() { my_smart_ptr a = foo(); foo2(a); } int main() { foo3(); } Это примерная реализация умных указателей. Суть работы их следующая. Есть некий счетчик ссылок, который является указателем на int. Так как это указатель, при копировании умного указателя счетчик копируется по ссылке(а не по значению), поэтому является общим для всех объектов. Пример класса является непотокобезопасным. Стандартные умные указатели являются потокобезопасными.

Комментариев нет:

Отправить комментарий