【问题标题】:copy constructor with template class not getting called没有调用模板类的复制构造函数
【发布时间】:2014-02-15 06:26:42
【问题描述】:

我试图编写一个示例代码来实现共享指针[仅供练习]。 在以下示例中,

  • 为什么编译器不抱怨修改other_T
  • 为什么没有调用复制构造函数SharedPtr(const T& other_T)? 这是代码sn-p。

    #include <iostream>
    using namespace std;
    #define DBG cout<<"[DEBUG]"<<__PRETTY_FUNCTION__<<endl
    class RefCount
    {
    protected:
        int m_ref;
        RefCount(){ DBG; m_ref = 1 ; }
        void reference(){ DBG; ++m_ref; }
        void dereference(){ DBG;--m_ref;    }
    };
    
     template <class T>
     class SharedPtr : public RefCount
     {
        T* m_T;
     public:
        SharedPtr() {  DBG; m_T = new T; }
        SharedPtr(const T& other_T){
            DBG;
            m_T = other_T.m_T;
            other_T.dereference();
            other_T.m_T = NULL;
        }
        ~SharedPtr() {
            DBG;
            dereference();
           cout<<m_ref<<endl;
           if(m_ref <= 0 && m_T != NULL ){
               cout<<"Destroying"<<endl;
               delete m_T;
               m_T = NULL;
            }
        }
    };
    
    class A{};
    int main()
    {
        SharedPtr<A> obj;
        cout<<"assigning "<<endl;
        SharedPtr<A> obj2 = obj;
        cout<<"END"<<endl;
        return 0;
    }
    

结果是段错误。

【问题讨论】:

  • 一方面,它应该是SharedPtr(const SharedPtr&lt;T&gt;&amp; other_T)。此外,您的复制构造函数不应该尝试修改other_T。你应该在做任何事情之前测试&amp;other_T是否与this相同。
  • @Apprentice 但不一定非得是const。还是您只是在说一般建议?
  • @remyabel 你为什么删除你的答案?没错。
  • @remyabel,是的,你是对的,它不一定是 const。但它是更好的,如果它不是,通常会是一个惊喜。他的复制构造函数看起来像一个移动而不是一个副本。

标签: c++ templates copy-constructor


【解决方案1】:

您的主要问题是复制构造函数 正在 被调用--但您尚未定义复制构造函数,因此您获得的是编译器默认定义的复制构造函数。

该复制构造函数只是按成员进行复制。这意味着您已经分配了一个Anew,然后将两个SharedPtr 对象指向同一个A。第一个被销毁的会删除A 对象。然后第二个对象被销毁,再次尝试删除同一个对象,结果随之而来。

最后,在我看来,这并没有太多(任何?)这将产生任何真正的影响。我很确定您的基本设计已损坏。要获得一个有效的共享指针,您需要一个引用计数和指向最终对象的“原始”指针。然后,您有 N 个 SharedPtr 对象引用该一个引用计数/指针结构,该结构又引用最终对象。

您正在尝试将原始指针/引用计数组合到单个 SharedPtr 中,但我没有看到任何实际可行的方法。

在我看来,您所谓的RefCount 的基本概念实际上是SharedPtr 设计的一部分。因此,我认为它的定义应该嵌套在 SharedPtr 的定义中(并且可能是私有的,因为外界没有理由知道它的存在,更不用说能够直接访问它了)。

考虑到这些因素,代码最终可能是这样的:

#include <iostream>
using namespace std;

#define DBG cout<<"[DEBUG]"<<__PRETTY_FUNCTION__<<endl

template <class T>
class SharedPtr {

    template <class U>
    struct Ref {
        mutable int m_ref;
        U *data;
        Ref(T *data) : m_ref(1), data(data) { DBG; }
        void add_ref() const { DBG; ++m_ref; std::cout << "m_ref=" << m_ref << "\n"; }
        void sub_ref() const { DBG; --m_ref; std::cout << "m_ref=" << m_ref << "\n"; }
        ~Ref() { delete data; }
    };

    Ref<T> *r;
public:
    SharedPtr(T *data) { DBG; r = new Ref<T>(data); }
    SharedPtr(SharedPtr const &p) : r(p.r) { DBG; r->add_ref(); }
    ~SharedPtr() {
        DBG;
        r->sub_ref();
        if (0 == r->m_ref) {
            delete r;
            std::cout << "deleted pointee\n";
        }
    }
};

class A{};

int main() {
    SharedPtr<A> obj(new A);
    cout<<"copying "<<endl;
    SharedPtr<A> obj2 = obj;
    cout<<"END"<<endl;
    return 0;
}

注意:尽管这至少修复了一些基本设计,但它仍然无法使用。它缺少取消引用运算符,因此您不能使用指针来获取它指向的值。它会在多线程环境中完全中断。我还没有考虑足够的问题来确定,但我的直接猜测是它可能也不是异常安全的。

【讨论】:

  • 是的,我同意。请投反对票。无论如何感谢您的回答。
  • @neeru:实际上,我投了赞成票。设计是有缺陷的,但你已经完成了研究并且很好地询问了它。如果其他人犯了同样的错误,我认为他们可能会发现您的问题很有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-22
  • 1970-01-01
  • 1970-01-01
  • 2016-04-13
  • 2015-09-30
  • 2011-07-15
  • 1970-01-01
相关资源
最近更新 更多