6

我用shared_ptr做了一些测试,我想不出下面的问题。我刚开始学习boost库。有人可以告诉我原因吗?

#include <boost\shared_ptr.hpp>
#include <iostream>

class A 
{
public:  
    virtual void sing()
    {
        std::cout<<"A";
    }
protected:  virtual ~A() {};

};

class B : public A 
{
public:  
    virtual void sing() 
    {   
        std::cout << "B"; 
    }
    virtual ~B() {};
};


int foo()
{   
    boost::shared_ptr<A> pa(new B());
    pa->sing();

    delete static_cast<B*>(pa.get());

    delete pa.get(); //this line has a problem error C2248: “A::~A”: can't access protected memmber(declared in class“A")   
    return 0;
}

int main()
{
    foo();
    return 0;
}

但是当该行被注释掉时,它可以被编译。当然,这并不意味着 shared_ptr 将删除内部维护的指针,就像我所做的那样。pa.get()返回的指针和内部维护的指针有什么区别吗?

4

4 回答 4

3

我相信这delete是在销毁shared_ptr传递给构造函数的指针类型的过程中调用的。看看这里的构造函数:

http://www.boost.org/doc/libs/1_49_0/libs/smart_ptr/shared_ptr.htm#constructors

因此,当您pa超出范围时,B::~B( )将调用而不是所包含类型的析构函数 - A::~A(这是不可能的,因为它已声明protected)。

于 2012-05-01T11:23:25.123 回答
2

实际上,它比这更复杂一点:背后的机器shared_ptr相当复杂。

首先,让我们证明没有授予以下特定访问权限shared_ptr

int main() {
    A* a = new B();
    std::shared_ptr<A> p(a); // expected-error
}

这将导致错误,因为 的析构函数A不可访问。有趣的是,错误发生在构造点,这是一个线索......

那么,背后的魔力是shared_ptr什么?

在内部,ashared_ptr保留的不仅仅是简单的指针和引用计数。Ashared_ptr是用 a 构建的deleter,负责破坏对象的实例。设计真正闪耀的地方在于它deleter在构造函数中被实例化,因此可能比裸shared_ptr类型知道更多的类型信息。

一个简化的演示:

template <typename T>
struct shared_holder {
    typedef void (*Disposer)(T*);

    explicit shared_holder_base(T* t, Disposer d): _ptr(t), _disposer(d) {}

    void dispose() { _disposer(_ptr); _ptr = 0; }

    T* _ptr;
    Disposer _disposer;
};

template <typename U, typename T>
void dispose(T* t) { delete static_cast<U*>(t); }

template <typename T>
class shared_ptr {
    typedef shared_holder<T> holder;
public:
    shared_ptr(): _holder(0), _ptr(0) {}

    template <typename U>
    explicit shared_ptr(U* u):
        _holder(new holder(u, dispose<U, T>)), _ptr(_holder->_ptr) {}

private:
    holder* _holder;
    T* _ptr;
};

关键的见解是处理程序是从构造函数已知的静态类型实例化的;这就是为什么:

  • shared_ptr<A>(new B)作品
  • A* a = new B; shared_ptr<A>(a)才不是

您可以阅读 Boost 标头,其背后的机制shared_ptr非常有趣。

作为读者的练习,为什么shared_ptr<T>会有一个_ptr成员?

于 2012-05-01T13:56:24.387 回答
1

当你有:

boost::shared_ptr<A> pa(new B());

...您正在调用boost::shared_ptr构造函数并正在处理两个模板参数:

  1. shared_ptr 模板类型TA在您的情况下);

    get()T*当您尝试时返回:

    delete pa.get();
    

    ...您试图~A()访问只有As 孩子可以访问的内容,因此出现错误。

    在以下行中:

     delete static_cast<B*>(pa.get());
    

    ...您正在向下转换A*B*调用删除,B*从而调用~B()您有权访问的。(~B()总是要求~A()~A()声明为virtual

  2. shared_ptr 构造函数参数模板类型YB在您的情况下)具有Y*必须可转换为的要求(在您的情况下,T*您可以向上 B*转换A*B继承A)。

    ~shared_ptr()调用(delete在您的情况下;可以访问并调用它),这就是超出范围时会发生的情况(这就是访问基类受保护的析构函数的方式,这是您最初的问题)。Y*B*~B()~A()pashared_ptr

boost::shared_ptr在内部保持一个指针。创建时boost::shared_ptr,您将向其构造函数传递一个SINGLE指针,该指针可以被视为/转换为指向这两种模板参数类型中的任何一种的指针

于 2012-05-01T12:29:25.620 回答
0

我相信这是因为 boost 模板的定义将 sharedpointer 声明为模板实例化中 <Class T> 类的朋友。

这是我从引导共享指针头文件中复制的片段。

// Tasteless as this may seem, making all members public allows member templates
// to work in the absence of member template friends. (Matthew Langston)

#ifndef BOOST_NO_MEMBER_TEMPLATE_FRIENDS

private:

    template<class Y> friend class shared_ptr;
    template<class Y> friend class weak_ptr;


#endif
于 2012-05-01T11:37:23.903 回答