boost的shared_ptr(shared_ptr<Y> const & r, T * p)用于什么?

26

boost::shared_ptr 有一个不同寻常的构造函数

template<class Y> shared_ptr(shared_ptr<Y> const & r, T * p);

我有点困惑这对于什么有用。基本上,它与r共享所有权,但.get()将返回p,而不是r.get()

这意味着你可以像这样做:

int main() {
    boost::shared_ptr<int> x(new int);
    boost::shared_ptr<int> y(x, new int);

    std::cout << x.get() << std::endl;
    std::cout << y.get() << std::endl;

    std::cout << x.use_count() << std::endl;
    std::cout << y.use_count() << std::endl;
}

你将得到这个:
0x8c66008
0x8c66030
2
2

请注意,指针是分开的,但它们都声称具有2个use_count(因为它们共享对同一对象的所有权)。
因此,由x拥有的int将存在,只要xy存在。如果我正确理解文档,第二个int永远不会被销毁。我已经用以下测试程序确认了这一点:
struct T {
    T() { std::cout << "T()" << std::endl; }
    ~T() { std::cout << "~T()" << std::endl; }
};

int main() {
    boost::shared_ptr<T> x(new T);
    boost::shared_ptr<T> y(x, new T);

    std::cout << x.get() << std::endl;
    std::cout << y.get() << std::endl;

    std::cout << x.use_count() << std::endl;
    std::cout << y.use_count() << std::endl;
}

这将输出(如预期):
T()
T()
0x96c2008
0x96c2030
2
2
~T()

所以...这种不寻常的结构共享一个指针的所有权,但在使用时像另一个指针(它不拥有)一样 起作用,那么它有什么用处呢?

3
简短版:它创建了一个指向r的子对象的指针。 - Ben Voigt
6个回答

29

当你想要共享一个类成员,而类的实例已经是 shared_ptr 时,使用它非常有用,例如:

struct A
{
  int *B; // managed inside A
};

shared_ptr<A>   a( new A );
shared_ptr<int> b( a, a->B );

它们共享使用计数和其他东西。这是为了优化内存使用。


1
一个好的答案。显然,在这个例子中,只要b存在,我们就希望保留a的对象。我认为我们有一个赢家。 - Evan Teran
不仅是为了优化内存使用,而且在特定的示例中,使用不同的方法可能会导致对 delete( a->B ) 的调用,这可能是意外的(考虑 struct A { int b; }; shared_ptr<A> a(new A); shared_ptr<int> b( a, &a->b ))。 - David Rodríguez - dribeas
它不仅仅是为了内存使用的优化。它可能有其他用途;) 请看下面我的回答。 - CoffeDeveloper

8

为了补充leizpiotr的回答,这里提供了一个关于shared_ptr<> '别名'的描述,摘自WG21的一篇论文"Improving shared_ptr for C++0x, Revision 2":

III. Aliasing Support

Advanced users often require the ability to create a shared_ptr instance p that shares ownership with another (master) shared_ptr q but points to an object that is not a base of *q. *p may be a member or an element of *q, for example. This section proposes an additional constructor that can be used for this purpose.

An interesting side effect of this increase of expressive power is that now the *_pointer_cast functions can be implemented in user code. The make_shared factory function presented later in this document can also be implemented using only the public interface of shared_ptr via the aliasing constructor.

Impact:

This feature extends the interface of shared_ptr in a backward-compatible way that increases its expressive power and is therefore strongly recommended to be added to the C++0x standard. It introduces no source- and binary compatibility issues.

Proposed text:

Add to shared_ptr [util.smartptr.shared] the following constructor:

template<class Y> shared_ptr( shared_ptr<Y> const & r, T * p );

Add the following to [util.smartptr.shared.const]:

template<class Y> shared_ptr( shared_ptr<Y> const & r, T * p );

Effects: Constructs a shared_ptr instance that stores p and shares ownership with r.

Postconditions: get() == p && use_count() == r.use_count().

Throws: nothing.

[Note: To avoid the possibility of a dangling pointer, the user of this constructor must ensure that p remains valid at least until the ownership group of r is destroyed. --end note.]

[Note: This constructor allows creation of an empty shared_ptr instance with a non-NULL stored pointer. --end note.]


4
您可以使用这个功能来保持动态转换的指针,例如:
class A {};
class B: public A {};

shared_ptr<A> a(new B);
shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));

另一个有趣的用途显然属于“别名”的范畴。好观点。 - Evan Teran

2

您可能有指向某个驱动程序或较低级别api的数据结构的指针,该结构可能通过其较低级别api或其他方式分配额外的数据。此时,增加use_count可能很有趣,但如果第一个指针拥有其他数据指针,则返回附加数据。


0

关于“shared_ptr<B> b(a, dynamic_cast<B*>(a.get()));

我认为这不是使用智能指针的推荐方式。

进行此类型转换的推荐方式应该是:

shared_ptr<B> b(a);

由于在Boost文档中提到:

shared_ptr<T>可以在T*可以隐式转换为U*的情况下,隐式转换为shared_ptr<U>。特别地,shared_ptr<T>可以隐式转换为shared_ptr<T> const,转换为T的可访问基类U的shared_ptr<U>,以及shared_ptr<void>

除此之外,我们还有dynamic_pointer_cast,它可以直接对智能指针对象进行转换,这两种方法都比手动转换原始指针的方式更安全。


0

我在我的小型库中使用了shared_ptr的别名构造函数:

http://code.google.com/p/infectorpp/(只是我的简单IoC容器)

问题在于,由于我需要从一个不知道类型的多态类返回已知类型的shared_ptr,我无法隐式地将shared_ptr转换为所需的类型。

在文件“InfectorHelpers.hpp”(第72-99行)中,您可以看到IAnyShared类型的操作。

别名构造函数创建的shared_ptr不会删除它们实际指向的指针,但它们仍然增加对原始对象的引用计数,这非常有用。

基本上,您可以使用别名构造函数创建指向任何内容的指针,并将其视为引用计数器。

//my class
std::shared_ptr<T> ist;
int a; //dummy variable. I need its adress

virtual std::shared_ptr<int> getReferenceCounter(){
    return std::shared_ptr<int>(ist,&a); //not intended for dereferencing
}

virtual void* getPtr(); //return raw pointer to T

现在我们既有“引用计数器”,又有指向T实例的指针,这些数据足以使用别名构造函数创建某些内容。
std::shared_ptr<T> aPtr( any->getReferenceCounter(), //share same ref counter 
               static_cast<T*>(any->getPtr()) ); //potentially unsafe cast!

我并不自称发明了别名构造函数的这种用法,但我从未见过其他人这样做。如果你在猜测那段肮脏的代码是否有效,答案是肯定的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接