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

436
12 июня 2018, 09:30

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

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

Answer 1

Простейший вариант - примерно так (это набросок, не более того):

class CountedPtr
{
    ...
        Type1 p1;  // Данные
        Type2 p2;  // Данные
        .... 
        int  * cnt;  // Счетчик

В конструкторе - помимо прочего -

cnt = new int(1);

В копирующем конструкторе - cnt копируется и увеличивается на 1. Соответственно обрабатывается присваивание (проще всего его реализовать через идиому обмена с копией). В деструкторе - что-то типа

if (--*cnt == 0) { delete ptr; delete cnt; }

Для начала (и понимания азов) хватит?

Answer 2

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

Answer 3

Вот у Страуструпа есть подсчет ссылок при реализации класса 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);
}
Answer 4

Я тоже оставлю здесь пример своей псевдореализации умного указателя, так как вопрос все-таки больше связан как реализовать самостоятельно, а не воспользоваться готовыми классами(хотя лучше пользоваться стандартными классами). Это всего лишь учебный пример, потому что Когда-то я также интересовался, как устроены умные указатели.

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. Так как это указатель, при копировании умного указателя счетчик копируется по ссылке(а не по значению), поэтому является общим для всех объектов.
Пример класса является непотокобезопасным. Стандартные умные указатели являются потокобезопасными.

READ ALSO
libmodbus передача пакета по RTU rs-485

libmodbus передача пакета по RTU rs-485

Кто работал с библиотекой libmodbus подскажите пожалуйстаХочу в устройство принимающая данный по протоколу MODBUS отправить сам пакет

215
Параметры функций

Параметры функций

Я хочу хочу понять какие параметры нужны в c++ в функции push_heap()Если можете объясните всё простым языком

154
Возврат из функции и её вызов в С++

Возврат из функции и её вызов в С++

Имею 2 функции (различий меж ними нет, практически) Правильно ли я выполняю из них возврат? И как правильно их нужно вызывать в main (между массивами,...

182
Ассемблер. Ассемблерные вставки. Строки

Ассемблер. Ассемблерные вставки. Строки

Как копировать одну строку в другуюНапример, строка text1[6]="fsdfaf" копируется в text2[6]={0}

187