Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/148.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/image-processing/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 具有可选所有权的智能指针_C++_Smart Pointers - Fatal编程技术网

C++ 具有可选所有权的智能指针

C++ 具有可选所有权的智能指针,c++,smart-pointers,C++,Smart Pointers,我试图允许一个类包含一个指针,它可以是一个拥有的指针,也可以是一个借用的指针。在前一种情况下,它应该销毁所拥有的对象本身;在后一种情况下,它不应该破坏指向的对象 在代码中,我有类A、B和C。我的目标是以下(简化)定义,其中B是需要拥有指针的类: class C { ... }; class B { C *c; B(C *c) : c(c) { } }; class A { C c1; B b1, b2; // b2 leaks poin

我试图允许一个类包含一个指针,它可以是一个拥有的指针,也可以是一个借用的指针。在前一种情况下,它应该销毁所拥有的对象本身;在后一种情况下,它不应该破坏指向的对象

在代码中,我有类A、B和C。我的目标是以下(简化)定义,其中B是需要拥有指针的类:

class C {
    ...
};

class B {
    C *c;
    B(C *c) : c(c) {
    }
};

class A {
    C c1;
    B b1, b2;
    // b2 leaks pointer to C
    A() : b1(&c1), b2(new C()) {
    }
};
A
的实例销毁时,它会销毁
c1
b1
b2
。理想情况下,
b2
的销毁应删除匿名
C
实例,但
b1
的销毁不应删除任何内容(因为
c1
将由A直接销毁)


我可以使用什么样的智能指针来实现这一点?或者,最好的解决方案是将所有权标志传递给B吗?

虽然我担心
B
可能会被滥用,但您可以这样做:

class B {
    C *c;
    bool owned;

    B(C& c) : c(&c), owned(false) {}
    B(C *c) : c(c), owned(true) {}
    ~B() { if (owned) delete c; }
};

class A {
    C c1;
    B b1, b2;
    A() : b1(c1), b2(new C()) {}
};

如果您确信并且能够保证重复使用的
C
不会被提前销毁(三次检查),那么有多种方法可以做到这一点。
您可能会考虑:

  • 您可以手动管理指针和标志。确保复制语义正确,例如:

    class B {
        std::unique_ptr<C> c;
        bool shared = false;
    
        B(C& c) : c(&c), shared(true) {}
        B(C *c = 0) : c(c) {}
        ~B() { if (shared) c.release(); }
    };
    
    template <class T> struct maybe_delete
    {
        void operator()(T* p) const noexcept {if(!shared) delete p;}
        bool shared = false;
    };
    template <class T> struct maybe_delete<T[]>
    {
        void operator()(T* p) const noexcept {if(!shared) delete [] p;}
        template <class U> void operator()(U*) const = delete;
        bool shared = false;
    };
    
    class B {
        std::unique_ptr<C, maybe_delete> c;
    
        B(C& c) : B(&c) {this->c.get_deleter().shared = true;}
        B(C *c) : c(c) {}
    };
    
    B类{
    std::唯一的ptr c;
    bool shared=false;
    B(C&C):C&C,共享(真){}
    B(C*C=0):C(C){}
    ~B(){if(共享)c.release();}
    };
    
  • 您可以使用自定义删除程序,如下所示:

    class B {
        std::unique_ptr<C> c;
        bool shared = false;
    
        B(C& c) : c(&c), shared(true) {}
        B(C *c = 0) : c(c) {}
        ~B() { if (shared) c.release(); }
    };
    
    template <class T> struct maybe_delete
    {
        void operator()(T* p) const noexcept {if(!shared) delete p;}
        bool shared = false;
    };
    template <class T> struct maybe_delete<T[]>
    {
        void operator()(T* p) const noexcept {if(!shared) delete [] p;}
        template <class U> void operator()(U*) const = delete;
        bool shared = false;
    };
    
    class B {
        std::unique_ptr<C, maybe_delete> c;
    
        B(C& c) : B(&c) {this->c.get_deleter().shared = true;}
        B(C *c) : c(c) {}
    };
    
    模板结构可能\u删除
    {
    void操作符()(T*p)const noexcept{if(!shared)delete p;}
    bool shared=false;
    };
    模板结构可能会被删除
    {
    void操作符()(T*p)const noexcept{if(!shared)delete[]p;}
    模板无效运算符()(U*)常量=删除;
    bool shared=false;
    };
    B类{
    std::唯一的ptr c;
    B(C&C):B(&C){this->C.get_deleter().shared=true;}
    B(C*C):C(C){}
    };
    
  • 您可以浏览一下std::shared_ptr,尽管这可能是严重的过度操作,可能会给您带来太多的开销

  • 据我所知,没有办法在没有副作用的情况下归档这种行为。如果只是普通的指针(不是COM),那么您可以通过两个类中的共享ptr访问C。如果只有B拥有C,那么他们两个都将被B的毁灭所摧毁。如果两个A&B都拥有C,那么C只有在最后一个活着的所有者(无论是A还是B)被销毁时才会被销毁

    我知道这种做法是为了考虑所有权: 若方法只得到一个普通指针,则意味着该指针将仅在该方法内部使用。因此,B将是:

    class B1 {
        B(C *c) {
          //do some staff with c
        }
        void doSomeStaff(C*) {}
    };
    
    或者使用&(如果您的框架接受,则使用cleaner):

    如果方法获得共享指针,则需要该指针以备将来重用(保留):

    B3级{
    公众:
    std::共享的ptr c;
    B(std::shared_ptr c):c(c){
    }
    };
    
    因此,现在您可以调用b1.doSomeStaff(b3.c)或b2.doSomeStaff(*b3.c),而无需考虑谁必须销毁指向的对象c。您只知道,此对象将在b1中使用。就这些

    不要忘记指定您需要的是shared_ptr,而不是方法中的C*——shared_ptr是一个对象,它在复制时会增加对对象的引用计数。并不是递增,而是在从C*构造时创建一个引用计数为1的新共享_ptr


    这不是你问题的答案,而是一些常见的用途。请参见答案中重复数据消除程序中的unique_ptr。另请检查:。即使不使用boost,也有一个很好的理论可以用来使用不同的方法来保持对象。同时检查以下答案:

    听起来像是噩梦。你如何解释这种“可选所有权”?指针对象是您独有的,还是您与其他人共享?线程安全?再进入者?还是你的一切?
    std::shared_ptr
    甚至允许这些恶作剧。但是如果与默认的deleter不同,您必须手动设置适当的deleter,比如空deleter。或者,使用一个指向所属对象的
    共享\u ptr
    ,以获得完全正确的语义。@重复数据消除程序删除程序是一个模板参数,因此您必须编写一个处理这两种情况的删除程序。你不能在一种情况下只传递一个空的删除器,在另一种情况下传递一个常规的删除器。要回答这个问题,“最好的解决方案是只传递一个所有权标志给B吗?”。是。@Adam:如何构造
    共享\u ptr
    的决定与传递所有权标志resp相同。更好的方法是按照我的第二种方式来构建它。