2017-05-28 139 views
15

为什么像shared_ptr这样的类在其构造函数中有另一个模板?通用复制构造函数

例如:

template<class T> class shared_ptr { 
public: 
    template<class Y> 
    explicit shared_ptr(Y * p); 

我一直在读斯科特迈尔斯的C++有效,项目45表示,它背后的想法是通过他们做出多态性可能;也就是说,从构建shared_ptr<B>shared_ptr<A>如果B从A

衍生而来,但没有定义诸如

explicit shared_ptr(T * p); 

足够的构造?我的意思是,这段代码工作得很好:

class C1 { 
}; 

class C2 : public C1 { 
}; 

template<typename T> 
class A 
{ 
public: 
    A(T &a) 
    { 
    var1 = a; 
    } 

    T var1; 
}; 

int main(int argc, char *argv[]) 
{ 
    C2 c2; 
    A<C1> inst1(c2); 
} 

那么为什么我们需要另一个构造函数模板?

+4

如果这两种类型不是使用继承而彼此派生的,但它们彼此之间可以转换* –

+1

@Someprogrammerdude如果它们可以相互转换,我的解决方案“无论如何工作?因为该类将被实例化为某种类型,并且会发生常规转换,因为这是一些T的具体类。 –

+1

即使两个不同类型的对象可以相互转换,但类型仍然不同。这意味着模板参数“T”必须表示两种不同的类型,这是不可能的。 –

回答

13

没有模板的构造,下面的代码将有不确定的操作:

#include <memory> 

class Base {}; 
class Derived : public Base {}; 

int main() { 
    std::shared_ptr<Base> ptr(new Derived); 
} 

如果shared_ptr只需Base*,它被强制为最终调用deleteBase*指针。但由于Base没有虚拟析构函数,指针实际指向Derived,这是未定义的行为。

但实际上,上面的代码格式良好。 shared_ptr的模板构造函数需要一个Derived*指针,并存储一个自定义的删除程序,该程序在原始的Derived*指针上调用delete,这很好。

+2

我认为这是一个很好的解释,为什么它需要'std :: shared_ptr',但我认为这种模式在标准库中的其他地方很常见,在这些地方不需要删除器。 –

+2

这是自定义删除者的一个相当边际的副作用。他们是一个很好的额外的东西,而不是shared_ptr背后的动力(这是共享指针)。 –

+0

@n.m .:对一个子对象拥有一个'shared_ptr'并且管理整个对象的生命周期的删除者的能力是'shared_ptr'的一个非常强大的功能。基础子对象只是一个特殊情况,其能力还包括成员子对象,甚至包含“拥有的”对象对象。 –

0
explicit shared_ptr(T * p); 

shared_ptr如果只有这个构造函数,它会破坏它的整个目的,是提供一种引用计数指针。

引用计数(或者说是指针)是shared_ptr的一部分。每次创建,销毁或复制shared_ptr时都应更新它。只有shared_ptr传递给复制例程才能实现最后一部分。传递一个裸指针将创建一个shared_ptr,引用计数为1.

引用计数的指针只在引用计数超过1时共享。对于您的计划,目前尚不清楚这是如何发生的。

一些引用计数功能由普通(非模板)复制构造函数提供。但不是全部。

例如,

std::shared_ptr<Derived> d; 
std_shared_ptr<Base> b; 
... 
b = d; 

会失败,但不会模板“副本的构造函数进行编译。我们希望共享poiters提供相同的多态行为正常的指针,所以我们要在上面建设只是工作。

+1

我想问题是为什么我们有'template 显式shared_ptr(Y *);',而不是为什么我们有shared_ptr(const shared_ptr&);和'template shared_ptr(const shared_ptr &);'。 – aschepler

+0

@aschepler我不认为这个问题完全意识到为什么shared_ptr需要一个正常的拷贝构造函数。无论如何,我会添加一些解释。 –

+0

@ n.m。首先感谢回应。但是,这些智能指针背后的想法不像具有某些附加功能的普通指针那样行为? (例如参考计数)。你写的代码,我认为其中一个应该是'd' –