我正忙于设计一个新的C ++应用程序。在这个应用程序中,我想用指针最小化潜在的错误,因为应用程序应该是普通的C ++(没有.Net或其他奇特的东西),我正在调查共享指针,我正在考虑在任何地方使用共享指针(而不是普通的指针)。
我制定了一些技巧,以便更容易使用共享指针,例如:在类中使用typedef,如下所示:
class X
{
public:
...
typedef std::shared_ptr<X> Ptr;
};
这样你就可以轻松编写X :: Ptr,它比“std :: shared_ptr”更容易编写。
我还注意到共享指针的一些缺点:
<memory>
是否还有其他技巧可以让共享指针更易于使用?
答案 0 :(得分:8)
不要!
最小化指针错误的唯一方法是使用正确的指针类型。那是类型,复数。
共享指针不是灵丹妙药。当你有周期性参考时,它们会很快变成内存泄漏(如果计划在任何地方使用它们,那么它们会很快出现)
如果你想要没有错误的C ++应用程序,你必须为它工作。你必须了解你的申请。您必须了解不同对象的所有权语义。共享指针只为您提供共享所有权,这通常是一个不错的最低分母。其他所有东西都可以被共享所有权取代,它会起作用。
但默认情况是对象由一个其他实体拥有。它由函数拥有,并且应该在该函数返回时被销毁,或者它由类或其他任何东西拥有。通常,你根本不需要指针。或许,对象按值存储在std::vector
中。或者它只是一个局部变量或类成员。如果 是一个指针,它通常会更好地由scoped_ptr
或者可能允许转让所有权(unique_ptr
或auto_ptr
)表示。< / p>
当您无法保证对象的生命周期或所有权时,
shared_ptr
就是您可能会遇到的问题。但是当你使用它时,你还需要使用weak_ptr
来打破周期。
实际上,更好的方法是尽可能地避免指针。当你做需要一个指针时,使用一个具有最特定所有权语义的指针(更喜欢scoped_ptr
,根本不允许转让所有权,然后如果你需要它,那么回到允许你移动所有权的一个,例如unique_ptr
,并且只作为最后的手段使用shared_ptr
,这允许您在任何数字之间自由地共享所有权客户。
没有魔法棒可以让你的C ++代码“正常工作”。实现这一目标的唯一方法是编写良好的可靠C ++代码。你通过了解和使用你可以使用的工具来做到这一点,而不是假装“嘿,shared_ptr
就像一个垃圾收集器,不是吗?我可以忽略对象生命周期的所有问题或者如果我使用它,内存泄漏“。
答案 1 :(得分:3)
不要只选择一个智能指针来随处使用。不是每个螺丝都是菲利普斯头。
此外,您可以使用任何智能指针的前向声明与原始指针完全相同:
struct X;
...
std::shared_ptr<X> ptr; // legal
struct X {};
ptr = std::shared_ptr<X>(new X); // legal
这是我第二次在SO上听到这种误解,而这只是100%错误。
答案 2 :(得分:1)
关于typedef
,您应该查看this question,它解决了完全相同的问题。
对于第二部分,我认为你错了,因为std::shared_ptr
可以用于N3225的20.9.10.2/2中规定的不完整类型:
T
的模板参数shared_ptr
可能是一个不完整的类型。
如果您当前的实施不支持,我认为应该将其视为错误。
答案 3 :(得分:1)
代码中很常见的是&#34;约定&#34;然后你跟随它,只要它在整个团队中一致地完成,人们就能理解它。
最好在&#34; forward&#34;中声明共享指针typedef。文件。像这样:
namespace boost { template< typename T > class shared_ptr; }
namespace MyStuff
{
class Foo;
class Bar;
class Baz;
typedef boost::shared_ptr<Foo> FooPtr;
typedef boost::shared_ptr<Bar> BarPtr;
typedef boost::shared_ptr<Baz> BazPtr;
}
您有时会想要使用除shared_ptr之外的其他指针,但您的约定是使用不同的表示形式,XPtr意味着shared_ptr。
当然,您可以使用其他符号。
我认为我们使用FooWeakPtr
来表示weak_ptr<Foo>
FooConstPtr
表示shared_ptr<const Foo>
例如。
应该在您的编码标准文件中。
答案 4 :(得分:0)
我更喜欢这种方法:
namespace Internal
{
template <class T>
struct DeclareShared
{
typedef std::shared_ptr<T> type;
};
template <class T>
struct DeclareUnique
{
typedef std::unique_ptr<T> type;
};
}
// Inherit one of these classes to use a generic smart pointer interface.
// If class is abstract, use this interface.
template <class T>
struct SharedAbstract
{
typedef typename Internal::DeclareShared<T>::type APtr;
};
template <class T>
struct Shared
{
typedef typename Internal::DeclareShared<T>::type Ptr;
template <class... P>
static Ptr shared(P&&... args)
{
return std::make_shared<T>(std::forward<P>(args)...);
}
};
template <class T>
struct UniqueAbstract
{
typedef typename Internal::DeclareUnique<T>::type AUPtr;
};
template <class T>
struct Unique
{
typedef typename Internal::DeclareUnique<T>::type UPtr;
template <class... P>
static UPtr shared(P&&... args)
{
return std::unique_ptr<T>(new T(std::forward<P>(args)...));
}
};
为scoped ptr等添加了更多接口,无论你想要什么,你都可以做以下事情:
struct Foo : public Shared<Foo>, public Unique<Foo>
{};
Foo::Ptr foo = Foo::shared();
Foo::UPtr unique = Foo::unique();
我不确定VS10如何应对可变参数模板,但这至少可以在GCC4.5 +上正常工作。