Запутался в подсчете ссылок

Спасибо за помощь заранее. Я читаю книгу Скотта Мейерса More Effective C++, но одна простая программа из пункта 29 "Подсчет ссылок" действительно смущает меня. Программа скопирована здесь:

String::String(const String& rhs): value(rhs.value) { ++value->refCount; }

Тогда код:

String s1("More Effective C++");
String s2=s1;

Я действительно сбит с толку, почему s1 и s2 будут иметь refCount 2. Я понимаю, что поскольку конструктор копирования передается по ссылке на const, после s2=s1,s2.refCount станет 2, а s1.refCount не изменится вообще. Пожалуйста, поправьте меня! Еще раз спасибо.

С уважением.

5 ответов

Я это понимаю s2.refCount станет 2, а s1.refCount не изменится вообще.

Есть ваше недоразумение. Там нет такого животного, как s2.refCount ни s1.refCount, Скорее переменные называются s2.value->refCount а также s1.value->refCount, Заметить, что s2.value == s1.valueпоэтому они по своей сути разделяют одно и то же refCount член.

value в этом случае указатель, и const -несс не распространяется на объект, на который указывают, поэтому refCount здесь изменчиво

Точка подсчета ссылок заключается в том, чтобы совместно использовать одно и то же представление объекта без его повторного создания до тех пор, пока не исчезнут все ссылки, т.е. На этом этапе представление отменено.

Это прекрасно работает для объектов, доступных только для чтения, поэтому, если один из ссылающихся экземпляров хочет изменить это общее представление, он обычно клонируется, и повторный подсчет начинается снова с одного.

Тогда возникают проблемы с тем, чтобы сделать подсчет ссылок потокобезопасным. Саттер много писал об этом, см. Getw # 43, gotw # 44 и gotw # 45.

Если счетчик ссылок используется s1 был 1, тогда он снесет строку с ним, когда он умер. Учтите следующее:

String s2;
{
    String s1("More Effective C++");
    s2 = s1;
} // A

В точке А s1 умирает. Если его refcount равен 1, он очистит хранилище, с которым он делился s2, а также s2 будет использовать недопустимое хранилище.

Счетчик ссылок не связан с каждым объектом. Как вы можете видеть из приведенного мною примера, это было бы бесполезно, потому что счетчик ссылок никогда не будет заслуживать доверия в качестве индикатора того, что его можно очистить.

Счетчик ссылок связан с частями хранилища, которые разделяют эти объекты. Существует только один счетчик ссылок для обоих s1 а также s2, Они разделяют часть хранилища с "Более эффективным C++" в нем. Это означает, что есть две ссылки на этот кусок памяти. Каждому из них нужно знать, что их два, чтобы они не очищали хранилище, которое использует другой.

Счетчик ссылок должен находиться в отдельной общей памяти:

struct Foo
{
    unsigned int * refcount;      // shared among all "equal" objects!

    Foo() : refcount(new auto {1U}) { }

    Foo(Foo const & rhs) : refcount(rhs.refcount) { ++*refcount; }

    ~Foo() { --*refcount; if (*refcount == 0) { delete refcount; } }

    Foo & operator=(Foo const & rhs)
    {
        if (this == std::addressof(rhs)) { return *this; }
        --*refcount;
        if (*refcount == 0) { delete refcount; }
        refcount = rhs.refcount;
        ++*refcount;
        return *this;
    }

    // etc.
};

value указатель на базовую структуру реализации Конструктор копирования строки копирует указатель в новый объект (s2) и увеличивает счетчик ссылок на указанную структуру реализации. Однако помните, что оригинальный объект (s1) имеет тот же указатель, поэтому счетчик ссылок, как видно из s1, будет также увеличен. Существует только одна базовая структура реализации, поэтому воздействие на нее из одного объекта String влияет на все другие объекты String, которые разделяют эту структуру реализации. Вот и весь смысл подсчета ссылок!

Другие вопросы по тегам