C++ собственный общий класс ptr

Я попытался написать свой собственный класс shared_ptr, простой, чтобы лучше понять его, вместе с конструктором перемещения и копирования и оператором присваивания перемещения и копирования. Это выглядит примерно так: -

// Online C++ compiler to run C++ program online
#include <iostream>
#include <stdio.h>
template<class T>
class my_shared_ptr
{
    private:
        T* ptr = nullptr;
        int* refcount = nullptr;
    
    public:
        my_shared_ptr() : ptr(nullptr),refcount(new int (0))
        {std::cout <<"Inside my_shared_ptr constructor \n" ;}
        
        my_shared_ptr(T* pT) : ptr(pT),refcount(new int (0))
        {std::cout <<"Inside my_shared_ptr with pT passed in constructor \n" ;}
        
        ~my_shared_ptr()
        {
            std::cout <<"Inside Destructor of my_shared_ptr \n";
            (*refcount)--;
            std::cout <<"Inside Destructor refcount is : "<< (*refcount) <<"\n";
            if ((*refcount) == 0)
            {
                std::cout <<"Deleting the one of the ptr whose reference count is now 0 \n";
                delete ptr;
            }   
        }
        
        // Copy constructor 
        
        my_shared_ptr(const my_shared_ptr& obj)
        {
            std::cout <<"Inside copy constructor call \n";
            this->ptr = obj.ptr;
            this->refcount = obj.refcount;
            
            if (obj.ptr != nullptr)
            {
                (*this->refcount)++;
            }
        }
        
        // Copy assignment operator 
        my_shared_ptr& operator=(const my_shared_ptr& obj)
        {
            std::cout <<"Inside copy assignment call \n";
            if (&obj == this )
                return *this;
            
            (*this->refcount)--;
            if ((*this->refcount) == 0)
            {
                delete (this->ptr);
            }
            this->ptr = obj.ptr;
            this->refcount = obj.refcount;
            
            if ( obj.ptr!= nullptr)
            {
                (*this->refcount)++;
            }
            return *this;
        }
        
        // Move constructor 
        my_shared_ptr(my_shared_ptr&& dyingobj) 
        {
            std::cout <<"Inside move constructor call \n";
            this->ptr = dyingobj.ptr;
            this->refcount = dyingobj.refcount;
            dyingobj.ptr = nullptr; // cleaning up the dying object
            dyingobj.refcount = nullptr;
        }
        
        // Move assignment
        my_shared_ptr& operator=(my_shared_ptr&& dyingobj)
        {
            std::cout <<"Inside move assignment call \n";
            if (&dyingobj == this )
            {
                std::cout <<"Two objects in move assignment are equal \n";
                return *this;
            }
            if ( (this) != &dyingobj )
            {
                (this->refcount)--;
                std::cout <<"Inside move assignment refcount is : "<< (*refcount) <<"\n";
                if ((this->refcount) == 0)
                {
                    std::cout <<"Deleting the previous shared object pointer whose reference count is now 0 \n";
                    delete this->ptr;
                }
            }
        
            this->ptr = dyingobj.ptr;
            this->refcount = dyingobj.refcount;
            dyingobj.ptr = nullptr; // cleaning up the dying object
            dyingobj.refcount = nullptr;
            return *this;
        }
        
        T* operator->() const
        {
            return ptr;
        }
        
        T& operator*() const
        {
            return *ptr;
        }
        
};

class Resource
{
    public:
        Resource()
        {
            std::cout <<"Resource Acquired \n"; 
        }
        
        ~Resource()
        {
            std::cout <<"Resource Destroyed \n";
        }
};

my_shared_ptr<Resource> generateResource()
{
    my_shared_ptr<Resource> res(new Resource());
    return res;
}

int main()
{
    std::cout <<"Before Creating mainres \n";
    my_shared_ptr<Resource> mainres = generateResource();
    std::cout <<"After Creating mainres \n";
    mainres = generateResource();
    std::cout <<"Before Creating mainres2 \n";
    my_shared_ptr<Resource> mainres2 = mainres;
    std::cout <<"After Creating mainres2 \n";
    {
        std::cout <<"Before Creating mainres3\n";
        my_shared_ptr<Resource> mainres3;
        mainres3 = mainres2;
        std::cout <<"After mainres3 assignment\n";
    }
    std::cout <<"mainres3 destroyed\n";
    //mainres = generateResource();
    return 0;
}

Вывод вышеуказанного кода:

Before Creating mainres 
Resource Acquired 
Inside my_shared_ptr with pT passed in constructor 
After Creating mainres 
Resource Acquired 
Inside my_shared_ptr with pT passed in constructor 
Inside move assignment call 
Inside move assignment refcount is : 0
Inside Destructor of my_shared_ptr 

Глядя на O/P, кажется, что код выполнялся нормально до mainres = generateResource();, но затем после этого он молча завершил работу. И я не получил никакого дальнейшего o/p. Не уверен, почему? Как правильно это исправить и где я ошибаюсь. Внутри вызова назначения перемещения я пытаюсь удалить уменьшение счетчика ссылок объекта my_shared_ptr, которому назначается другой my_shared_ptr, и проверить, равен ли счетчик ссылок 0, а затем удалить ptr этого объекта my_shared_ptr. Я что-то пропустил здесь?

Заранее спасибо.

Поможет ли это: godbolt.org/z/v8oKoKxx9 ?

user4581301 31.03.2023 22:24

Напечатайте адреса в деструкторе, и вы можете заметить проблему.

Yksisarvinen 31.03.2023 22:26

Если вы собираетесь печатать сообщения в конструкторах и деструкторах класса, простой вывод простых сообщений мало поможет. Намного больше помогает распечатать значение this вместе с сообщением. Иначе вы не знаете, что на самом деле строится и разрушается.

PaulMcKenzie 31.03.2023 22:32

Немного иронии... у тебя тоже утечка памяти

ChrisMM 31.03.2023 22:34

Помимо прочего, оператор присваивания уменьшает счетчик ссылок, даже если ptr имеет значение null. Это приведет к отрицательному значению счетчика ссылок, и ситуация выйдет из-под контроля.

Raymond Chen 31.03.2023 22:54

Кстати, вы никогда не delete refcount; , что является утечкой памяти для каждого общего объекта. Более того, вы должны сначала начать с правила 3, а затем оптимизировать с помощью правила 5. То есть не спешите определять конструктор перемещения + назначение перемещения. В качестве начального шага гораздо проще и безопаснее реализовать правило 3 с идиомой копирования + подкачки; другие подходы включают в себя оптимизацию, которую сейчас делать слишком рано.

Red.Wave 31.03.2023 22:56

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

Invictus 31.03.2023 23:01

@Red.Wave Да, идея состоит в том, чтобы использовать правило 5 везде, где мы можем, это было просто, чтобы попробовать кое-что самостоятельно. Да, я пропустил удаление счетчика ссылок, спасибо за это.

Invictus 31.03.2023 23:04

Я не уверен, какую проблему вы заметили, но мой комментарий был о том, что вы полагаетесь на тот факт, что refcount никогда не бывает nullptr, но затем вы обнуляете обе свои операции перемещения. И затем вы пытаетесь получить к нему доступ в деструкторе, чтобы уменьшить его, но он равен нулю.

Yksisarvinen 31.03.2023 23:05
my_shared_ptr::my_shared_ptr(T* pT) начинается со счетчика ссылок 0 вместо 1. Если объект, созданный с помощью этого конструктора, удаляется, вы уменьшаете счетчик ссылок до -1, т.е. объект не будет удален в этом случае или будет удален раньше, если вы правильно скопируете смарт указатель...
fabian 31.03.2023 23:21
(this->refcount)--; Ваш оператор присваивания перемещения уменьшает указатель refcount вместо объекта, на который он указывает.
Miles Budnek 31.03.2023 23:24

@Invictus, я имею в виду делать все, но шаг за шагом. Так вы получите четкое понимание. 1-й примените правило 3 с назначением копирования и конструктором (сделайте копирование/своп). Затем улучшите крайние случаи с тщательной реализацией назначения перемещения и конструктора. Делайте это, но терпеливо, стабильными шагами.

Red.Wave 31.03.2023 23:27

@JesperJuhl, как попасть в Карнеги-холл?

user4581301 01.04.2023 00:02

@Yksisarvinen да, спасибо за этот вклад, да, я изменил код, чтобы убедиться, что везде выполняются проверки на nullptr и т. д., и это было полезно.

Invictus 01.04.2023 10:41

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

Invictus 01.04.2023 10:42

@Red.Wave Спасибо за ваш вклад и поддержку

Invictus 01.04.2023 10:43

Недавно я написал статью об умных указателях, я думаю, она может помочь вам понять, что происходит за кулисами: cppsenioreas.wordpress.com/2023/02/21/… способы.

Coral Kashri 01.04.2023 14:57
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
17
151
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Всегда очень полезно узнать о копировании и перемещении класса следующим образом:

#include <iostream>

struct Verbose {
    Verbose() {
        std::cout << "   Verbose default construct\n";
    }

    Verbose(Verbose const&) {
        std::cout << "   Verbose copy construct\n";
    }

    Verbose(Verbose&&) {
        std::cout << "   Verbose move construct\n";
    }

    ~Verbose() {
        std::cout << "   Verbose destruct\n";
    }

    Verbose& operator=(Verbose const&) {
        std::cout << "   Verbose copy assign\n";
        return *this;
    }

    Verbose& operator=(Verbose&&) {
        std::cout << "   Verbose move assign\n";
        return *this;
    }
};

Это позволяет вам отслеживать, что именно вызывается в выводе. Между вызовами вы должны вставить дополнительный вывод, чтобы иметь возможность распознать из вывода, где вы находитесь внутри main().

int main() {
    std::cout << "Default construct a\n";
    auto a = Verbose();
    std::cout << "address of a is: " << &a << '\n';

    std::cout << "Copy construct b from a\n";
    auto b = a;
    std::cout << "address of a is: " << &a << '\n';
    std::cout << "address of b is: " << &b << '\n';

    std::cout << "Move construct c from a\n";
    auto c = std::move(a);
    std::cout << "address of a is: " << &a << '\n';
    std::cout << "address of b is: " << &b << '\n';
    std::cout << "address of c is: " << &c << '\n';
}
Default construct a
   Verbose default construct
address of a is: 0x7ffd1a93cdf5
Copy construct b from a
   Verbose copy construct
address of a is: 0x7ffd1a93cdf5
address of b is: 0x7ffd1a93cdf6
Move construct c from a
   Verbose move construct
address of a is: 0x7ffd1a93cdf5
address of b is: 0x7ffd1a93cdf6
address of c is: 0x7ffd1a93cdf7
   Verbose destruct
   Verbose destruct
   Verbose destruct

Если эта часть вам понятна, то далее вы можете использовать класс в SmartPointer, чтобы лучше понять его:

#include <memory>

int main() {
    std::cout << "Default construct shared a\n";
    auto a = std::make_shared<Verbose>();
    std::cout << "shared a points to: " << a.get() << '\n';

    std::cout << "Copy construct shared b from shared a\n";
    auto b = a;
    std::cout << "shared a points to: " << a.get() << '\n';
    std::cout << "shared b points to: " << b.get() << '\n';

    std::cout << "Move construct shared c from shared a\n";
    auto c = std::move(a);
    std::cout << "shared a points to: " << a.get() << '\n';
    std::cout << "shared b points to: " << b.get() << '\n';
    std::cout << "shared c points to: " << c.get() << '\n';
}
Default construct shared a
   Verbose default construct
shared a points to: 0x559a146ae2d0
Copy construct shared b from shared a
shared a points to: 0x559a146ae2d0
shared b points to: 0x559a146ae2d0
Move construct shared c from shared a
shared a points to: 0
shared b points to: 0x559a146ae2d0
shared c points to: 0x559a146ae2d0
   Verbose destruct

Вы также можете дать классу Verbose идентификатор для отслеживания отдельных объектов.

struct Verbose {
    std::string id_ = "nameless";

    void rename(std::string id) {
        std::cout << "   Verbose rename from " << id_ << " to " << id << "\n";
        id_ = std::move(id);
    }

    Verbose() {
        std::cout << "   Verbose default construct " << id_ << "\n";
    }

    Verbose(Verbose const& o) {
        std::cout << "   Verbose copy construct " << id_ << "< = " << o.id_ << "\n";
    }

    Verbose(Verbose&& o) {
        std::cout << "   Verbose move construct " << id_ << "< = " << o.id_ << "\n";
    }

    ~Verbose() {
        std::cout << "   Verbose destruct " << id_ << "\n";
    }

    Verbose& operator=(Verbose const& o) {
        std::cout << "   Verbose copy assign " << id_ << "< = " << o.id_ << "\n";
        return *this;
    }

    Verbose& operator=(Verbose&& o) {
        std::cout << "   Verbose move assign " << id_ << "< = " << o.id_ << "\n";
        return *this;
    }
};

Обратите внимание, что идентификатор меняется только с помощью функции rename()!

int main() {
    std::cout << "Default construct of a\n";
    auto a = Verbose();
    a.rename("a");
    std::cout << "address of a is: " << &a << '\n';

    std::cout << "Copy construct b from a\n";
    auto b = a;
    b.rename("b");
    std::cout << "address of a is: " << &a << '\n';
    std::cout << "address of b is: " << &b << '\n';

    std::cout << "Move construct c from a\n";
    auto c = std::move(a);
    c.rename("c");
    std::cout << "address of a is: " << &a << '\n';
    std::cout << "address of b is: " << &b << '\n';
    std::cout << "address of c is: " << &c << '\n';
}
Default construct of a
   Verbose default construct nameless
   Verbose rename from nameless to a
address of a is: 0x7ffee311aba0
Copy construct b from a
   Verbose copy construct nameless<=a
   Verbose rename from nameless to b
address of a is: 0x7ffee311aba0
address of b is: 0x7ffee311abc0
Move construct c from a
   Verbose move construct nameless<=a
   Verbose rename from nameless to c
address of a is: 0x7ffee311aba0
address of b is: 0x7ffee311abc0
address of c is: 0x7ffee311abe0
   Verbose destruct c
   Verbose destruct b
   Verbose destruct a

Этот пример также хорошо показывает порядок вызовов деконструктора. Это прямо противоположно порядку построения.

Вот еще одна упрощенная реализация SharedPtr, которая также выполняет обширный вывод:

struct SharedState {
public:
    SharedState(Verbose* ptr): ptr_(ptr), count_(1) {
        std::cout << "  SharedState construct " << id() << "\n";
    }

    ~SharedState() {
        std::cout << "  SharedState destruct " << id() << "\n";
        delete ptr_;
    }

    SharedState(SharedState const& o) = delete;
    SharedState(SharedState&& o) = delete;
    SharedState& operator=(SharedState const& o) = delete;
    SharedState& operator=(SharedState&& o) = delete;

    std::size_t increase() {
        return ++count_;
    }

    std::size_t decrese() {
        return --count_;
    }

    Verbose* get() const {
        return ptr_;
    }

    std::string id() const {
        return ptr_->id_ + "$" + std::to_string(count_);
    }

private:
    Verbose* ptr_;
    std::size_t count_;
};

class SharedPtr {
public:
    std::string id() const {
        using namespace std::literals;
        return id_ + "->" + (state_ ? state_->id() : "nullptr"s);
    }

    void rename(std::string id) {
        std::cout << " SharedPtr rename from " << this->id_ << " to " << id << "\n";
        id_ = std::move(id);
    }

    SharedPtr(): state_(nullptr)  {
        std::cout << " SharedPtr default construct " << id() << "\n";
    }

    SharedPtr(Verbose* ptr): state_(new SharedState(ptr)) {
        std::cout << " SharedPtr direct construct " << id() << "\n";
    }

    SharedPtr(SharedPtr const& o) {
        std::cout << " SharedPtr copy construct " << id() << "< = " << o.id() << "\n";
        setSharedState(o.state_);
        std::cout << " ======================== " << id() << "< = " << o.id() << "\n";
    }

    SharedPtr(SharedPtr&& o) {
        std::cout << " SharedPtr move construct " << id() << "< = " << o.id() << "\n";
        setSharedState(o.state_);
        o.removeSharedState();
        std::cout << " ======================== " << id() << "< = " << o.id() << "\n";
    }

    ~SharedPtr() {
        std::cout << " SharedPtr destruct " << id() << "\n";
        removeSharedState();
    }

    SharedPtr& operator=(SharedPtr const& o) {
        std::cout << " SharedPtr copy assign " << id() << "< = " << o.id() << "\n";
        removeSharedState();
        setSharedState(o.state_);
        std::cout << " ===================== " << id() << "< = " << o.id() << "\n";
        return *this;
    }

    SharedPtr& operator=(SharedPtr&& o) {
        std::cout << " SharedPtr move assign " << id() << "< = " << o.id() << "\n";
        removeSharedState();
        setSharedState(o.state_);
        o.removeSharedState();
        std::cout << " ===================== " << id() << "< = " << o.id() << "\n";
        return *this;
    }

    Verbose* get() const {
        return state_ ? state_->get() : nullptr;
    }

    Verbose* operator->() const {
        if (!state_) {
            throw std::logic_error("dereference nullptr");
        }
        return state_->get();
    }

private:
    void removeSharedState() {
        if (state_) {
            if (state_->decrese() == 0) {
                delete state_;
            }
            state_ = nullptr;
        }
    }

    void setSharedState(SharedState* state) {
        state_ = state;
        if (state_) {
            state_->increase();
        }
    }

    std::string id_ = "Nameless";
    SharedState* state_ = nullptr;
};

int main() {
    std::cout << "Default construct A\n";
    auto A = SharedPtr(new Verbose);
    A->rename("a");
    A.rename("A");
    std::cout << A.id() << " points to: " << A.get() << '\n';

    std::cout << "Copy construct B from A\n";
    auto B = A;
    B.rename("B");
    std::cout << A.id() << " points to: " << A.get() << '\n';
    std::cout << B.id() << " points to: " << B.get() << '\n';

    std::cout << "Move construct C from A\n";
    auto C = std::move(A);
    C.rename("C");
    std::cout << A.id() << " points to: " << A.get() << '\n';
    std::cout << B.id() << " points to: " << B.get() << '\n';
    std::cout << C.id() << " points to: " << C.get() << '\n';
}
Default construct A
   Verbose default construct nameless
  SharedState construct nameless$1
 SharedPtr direct construct Nameless->nameless$1
   Verbose rename from nameless to a
 SharedPtr rename from Nameless to A
A->a$1 points to: 0x565113a0e2c0
Copy construct B from A
 SharedPtr copy construct Nameless->nullptr<=A->a$1
 ======================== Nameless->a$2<=A->a$2
 SharedPtr rename from Nameless to B
A->a$2 points to: 0x565113a0e2c0
B->a$2 points to: 0x565113a0e2c0
Move construct C from A
 SharedPtr move construct Nameless->nullptr<=A->a$2
 ======================== Nameless->a$2<=A->nullptr
 SharedPtr rename from Nameless to C
A->nullptr points to: 0
B->a$2 points to: 0x565113a0e2c0
C->a$2 points to: 0x565113a0e2c0
 SharedPtr destruct C->a$2
 SharedPtr destruct B->a$1
  SharedState destruct a$0
   Verbose destruct a
 SharedPtr destruct A->nullptr

Если вы хотите использовать SharedPtr для произвольных типов, вам нужно создать шаблоны SharedPtr и SharedState. Обратите внимание, что это больше не предполагает, что тип T (ранее Verbose) имеет идентификатор. Поэтому он больше не может отображаться как часть идентификатора SharedPtr и SharedState. Это основная причина, по которой я сразу не показал универсальную версию.

Следующий diff показывает места, которые вам нужно изменить. Строки, которые начинаются с минуса. Строки, которые начинаются с плюса. (К сожалению, в настоящее время StackOverflow не поддерживает подсветку синтаксиса для этого).

diff --git a/main.cpp b/main.cpp
index 2a511ea..b02f931 100644
--- a/main.cpp
+++ b/main.cpp
@@ -36,9 +36,10 @@ struct Verbose {
     }
 };
 
+template <typename T>
 struct SharedState {
 public:
-    SharedState(Verbose* ptr): ptr_(ptr), count_(1) {
+    SharedState(T* ptr): ptr_(ptr), count_(1) {
         std::cout << "  SharedState construct " << id() << "\n";
     }
 
@@ -60,19 +61,20 @@ public:
         return --count_;
     }
 
-    Verbose* get() const {
+    T* get() const {
         return ptr_;
     }
 
     std::string id() const {
-        return ptr_->id_ + "$" + std::to_string(count_);
+        return "ref_count$" + std::to_string(count_);
     }
 
 private:
-    Verbose* ptr_;
+    T* ptr_;
     std::size_t count_;
 };
 
+template <typename T>
 class SharedPtr {
 public:
     std::string id() const {
@@ -89,7 +91,7 @@ public:
         std::cout << " SharedPtr default construct " << id() << "\n";
     }
 
-    SharedPtr(Verbose* ptr): state_(new SharedState(ptr)) {
+    SharedPtr(T* ptr): state_(new SharedState<T>(ptr)) {
         std::cout << " SharedPtr direct construct " << id() << "\n";
     }
 
@@ -128,11 +130,11 @@ public:
         return *this;
     }
 
-    Verbose* get() const {
+    T* get() const {
         return state_ ? state_->get() : nullptr;
     }
 
-    Verbose* operator->() const {
+    T* operator->() const {
         if (!state_) {
             throw std::logic_error("dereference nullptr");
         }
@@ -149,7 +151,7 @@ private:
         }
     }
 
-    void setSharedState(SharedState* state) {
+    void setSharedState(SharedState<T>* state) {
         state_ = state;
         if (state_) {
             state_->increase();
@@ -157,12 +159,12 @@ private:
     }
 
     std::string id_ = "Nameless";
-    SharedState* state_ = nullptr;
+    SharedState<T>* state_ = nullptr;
 };
 
 int main() {
     std::cout << "Default construct A\n";
-    auto A = SharedPtr(new Verbose);
+    auto A = SharedPtr<Verbose>(new Verbose);
     A->rename("a");
     A.rename("A");
     std::cout << A.id() << " points to: " << A.get() << '\n';

Я не проверял операторы присваивания, пожалуйста, дайте мне знать, если что-то нужно изменить!

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

Invictus 01.04.2023 11:25

Я думаю, что было бы лучше, если бы SharedState и SharedPtr использовали шаблон <typename T> вместо Verbose. Поскольку это сделало бы более общим для людей использование этого с любым классом, а не специфичным для Verbose.

Invictus 01.04.2023 12:14

@Invictus Это было частью упрощения. Вы можете просто заменить все Verbose в SharedState и SharedPtr на T и сделать их оба шаблоном. Но вывод ID Verbose как части ID SharedPtr и SharedState вылетает! Если есть какие-либо проблемы, просто дайте мне знать ;-)

Benjamin Buch 01.04.2023 12:52

Я попытался сделать это, объявив их как template<class T>, а затем заменив появление Verbose в обоих классах на T. Но я начал получать ошибку: main.cpp:158:25: error: тип заполнителя шаблона 'SharedState<. ..auto...>' должен сопровождаться простым идентификатором декларатора 158 | недействительным setSharedState (состояние SharedState *) { | ^~~~~~~~~~~ main.cpp:41:8: примечание: здесь объявлена ​​«шаблонная структура SharedState» 41 | структура SharedState

Invictus 01.04.2023 13:37

@Invictus Я добавил Diff со строками, которые нужно изменить. Вот живой пример с обобщенным кодом: godbolt.org/z/bYahe3Wjq

Benjamin Buch 01.04.2023 14:00

Спасибо, Бенджамин, я понял, что мое знание синтаксиса шаблонов вызывает здесь проблему. Просто быстрый вопрос: похоже, что сохранение отдельного класса состояния более полезно, особенно при обработке случаев назначения перемещения, где вы можете удалить состояние отдельно или установить для него нули, что, казалось бы, было бы сложно, если бы этот счетчик ссылок был частью общего сам класс указателя. Верно ?

Invictus 01.04.2023 17:06

Наличие всех общих данных в классе SharedState имеет то преимущество, что копирование и перемещение проще реализовать, а SharedPtr использует меньше памяти (только один элемент данных). Большим недостатком является то, что для каждого доступа к данным необходимо два разыменования указателя. Реальные реализации, вероятно, разделят только количество, потому что небольшое преимущество в размере памяти менее важно, чем очень большой недостаток во времени доступа. sizeof(std::shared_ptr<int>) равно 16 для x64 libstdc++, что указывает на то, что он содержит один указатель на объект и один на другие данные (счетчик + объект удаления).

Benjamin Buch 02.04.2023 12:34

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