C++自己的共享ptr类

mlmc2os5  于 2023-04-08  发布在  其他
关注(0)|答案(1)|浏览(107)

我试着写了一个自己的shared_ptr类,一个简单的类,以便更好地掌握它沿着move & copy构造函数和move & copy赋值运算符。它看起来像下面这样:

// 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_的my_shared_ptr对象的引用计数ptr并检查引用计数是否为0,然后删除my_shared_ptr对象的ptr。我在这里遗漏了什么吗?
先谢谢你了。

kqlmhetl

kqlmhetl1#

要学习复制和移动类,如下所示总是非常有用的:

#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类指定一个ID来跟踪各个对象。

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;
    }
};

请注意,ID只能通过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,您必须创建SharedPtrSharedState模板。注意,这不再假设T类型(以前的Verbose)有一个ID。因此,它不能再显示为SharedPtrSharedState的ID的一部分。这是我没有立即展示通用版本的主要原因。
下面的diff显示了需要更改的地方。以minus out开头的行。以plus in开头的行。(不幸的是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';

我还没有测试赋值运算符,如果需要调整,请告诉我!

相关问题