Как реализовать подсчет ссылок при создании собственного класса C++?
То есть нужен счетчик, который хранил бы количество ссылок, ссылающихся на объект. Если этот счетчик равен нулю, то объект необходимо удалить. Хочется разобраться в этом, но не имею ни малейшей мысли как это реализовать.
Простейший вариант - примерно так (это набросок, не более того):
class CountedPtr
{
...
Type1 p1; // Данные
Type2 p2; // Данные
....
int * cnt; // Счетчик
В конструкторе - помимо прочего -
cnt = new int(1);
В копирующем конструкторе - cnt
копируется и увеличивается на 1. Соответственно обрабатывается присваивание (проще всего его реализовать через идиому обмена с копией). В деструкторе - что-то типа
if (--*cnt == 0) { delete ptr; delete cnt; }
Для начала (и понимания азов) хватит?
Все уже реализовано: используйте std::shared_ptr
.
Вот у Страуструпа есть подсчет ссылок при реализации класса String.
// String.h
#include <cstring>
#include <iostream>
#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);
}
Я тоже оставлю здесь пример своей псевдореализации умного указателя, так как вопрос все-таки больше связан как реализовать самостоятельно, а не воспользоваться готовыми классами(хотя лучше пользоваться стандартными классами). Это всего лишь учебный пример, потому что Когда-то я также интересовался, как устроены умные указатели.
template <class T>
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> 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<example> foo()
{
auto t = my_smart_ptr<example>(new example(10));
int a = 10 + 5;
a += 5;
return t;
}
void foo2(my_smart_ptr<example> ex)
{
ex->setA(5);
}
void foo3()
{
my_smart_ptr<example> a = foo();
foo2(a);
}
int main()
{
foo3();
}
Это примерная реализация умных указателей. Суть работы их следующая.
Есть некий счетчик ссылок, который является указателем на int. Так как это указатель, при копировании умного указателя счетчик копируется по ссылке(а не по значению), поэтому является общим для всех объектов.
Пример класса является непотокобезопасным. Стандартные умные указатели являются потокобезопасными.
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Кто работал с библиотекой libmodbus подскажите пожалуйстаХочу в устройство принимающая данный по протоколу MODBUS отправить сам пакет
Я хочу хочу понять какие параметры нужны в c++ в функции push_heap()Если можете объясните всё простым языком
Имею 2 функции (различий меж ними нет, практически) Правильно ли я выполняю из них возврат? И как правильно их нужно вызывать в main (между массивами,...
Как копировать одну строку в другуюНапример, строка text1[6]="fsdfaf" копируется в text2[6]={0}