你们在typedef shared_ptr时遵循什么约定?

54

我在为boost::shared_ptr模板进行typedef的命名约定方面犹豫不决。例如:

typedef boost::shared_ptr<Foo> FooPtr;

在制定规则之前,我想了解其他人使用的约定是什么?你们使用的约定是什么?

编辑:

对于那些将typedef嵌套在Foo内部的人,你们不会感到困扰吗?现在Foo“知道”它将如何传递。这似乎破坏了封装。你觉得这样怎么样:

class Foo
{
public:
    typedef std::vector<Foo> Vector;
};

现在你不会这样做了,对吧?:-)


6
除了这个问题之外,当你声明这样的typedef时,人们如何管理头文件的包含?这不会导致每个人都必须包含Foo.h(而不仅仅是说“class Foo;”),从而导致构建速度变慢,可能会出现循环依赖关系。 - Rob Napier
1
我认为如果您使用工厂,嵌套 typedef 不会破坏封装性。实际上,在这种情况下,我会说它提高了封装性,以防指针类型发生更改。 - rlbond
@RobNapier #include "foo_ptr.hpp" ;-)`(请注意,这是一段程序代码,不应进行翻译) - Emile Cormier
@RobNapier 我使用宏MYLIB_DECLARE_PTRS(Foo);来替换class Foo;。这个想法来源于Pixar的USD中使用的约定 --- 我非常喜欢这个约定。 - Boris Dalstein
17个回答

28

我希望为这个旧问题添加一些选项,尽管它们可能会引起很大争议...

类似于OldPeculier的答案,我喜欢短型名称,尽可能地与标准指针相似。

在一个几乎到处都使用shared_pointer的项目中,我使用了

typedef boost::shared_ptr<Foo> Foo_;

// usage examples:
Foo* myFoo0;
Foo_ myFoo1;

我利用了三个方面:

  1. 下划线字符看起来像运算符,但大多数情况下被视为字母的一部分,因此它可以成为标识符的一部分(而且我在没有规则禁止在标识符结尾处使用它)。
  2. 我只需要想出一个typedef。
  3. 出于几个原因,我更喜欢Foo* myFoo1;而不是Foo *myFoo1;,这与Foo_ myFoo2很匹配。

当需要不同种类的智能指针typedef时,我会选择

typedef shared_ptr<Foo> Foo_S;
typedef weak_ptr<Foo>   Foo_W;
typedef unique_ptr<Foo> Foo_U;

// usage examples:
Foo*  myFoo2;
Foo_S myFoo3;
Foo_W myFoo4;
Foo_U myFoo5;

随着标准和编译器实现对Unicode的支持越来越多,我会尝试以下语法,假设星号字符将被视为类型标识符的常规部分。当然,这只有在所有涉及的开发者都有方便的文本输入方法时才是实用的:

typedef shared_ptr<Foo> Foo★;
typedef weak_ptr<Foo>   Foo☆;
typedef unique_ptr<Foo> Foo✪;

// usage examples:
Foo* myFoo6;
Foo★ myFoo7;
Foo☆ myFoo8;
Foo✪ myFoo9;

一个快速的测试表明,这实际上并不起作用,至少对于我的构建环境来说是如此。但是对于Foo_ä也是如此。


2
作为这篇回答的作者,我想披露一下我在 Twitter 上发布了一个链接和最后一个代码示例的截图。由于代码看起来非常有趣,推文获得了很高的人气——超过150次转发和50,000多次浏览——因此相对较高的赞数可能并不具有代表性。 - Lena Schimmel
请注意,int* a, b 看起来可能像 ab 都是指向 int 的指针。然而,事实并非如此:只有 a 是指向 int 的指针,b 只是一个 int。这就是为什么我更喜欢 int *a, b,它(可能)有助于避免混淆的原因之一。还有一件事:如果你有 typedef int *PTR_INT;PTR_INT a, b,那么 ab 都是 PTR_INT,即指向 int 的指针。 - Milan

28

回答:不要这样做。这只是对你方便而已,对别人没有好处。直接表达你的意思吧。


3
我同意。在这种情况下,typedef 只是为了节省打字,并没有其他作用。它只增加了更多的间接操作来理解代码。 - Paul Manta

15

我的偏好:

class Foo
{
public:

    typedef boost::shared_ptr<Foo> SharedPointer;
};

仅使用FooPtr存在问题,因为您可能有不同类型的指针(例如,weak_ptr)。我也不太喜欢缩写,但那是另一回事。


2
正如评论中指出的那样,这会导致头文件包含的问题。如果不包含“Foo.h”,你将如何在头文件中使用“Foo::SharedPointer”? - betabandido

5
在我负责的代码中,通常会在与Foo相同的命名空间范围内看到一个FooPtr typedef,并且Foo将包含一个通用命名为'SmartPtr'的typedef,指向与FooPtr相同的类型。拥有FooPtr可以轻松地使用非冗长的手动方式。具有“SmartPtr”或某些等效项的嵌套typedef允许在模板、宏等中轻松进行通用使用,而无需知道智能指针的实际类型。
此外,我建议将“主观”标签添加到这个问题中。

我认为像主观这样的元标记现在已经不被鼓励使用了 - http://blog.stackoverflow.com/2010/08/the-death-of-meta-tags/ - SCFrench

4

通常我不喜欢使用非常短的标识符,但是在这种情况下,我会使用它们。

class Foo
{
public:
    typedef std::shared_ptr<Foo> p;
};

这使得 shared_ptr 尽可能地类似于普通指针,而不会产生混淆。
Foo* myFoo;
Foo::p myFoo;

至于破坏封装性,将shared_ptr类型在类内typedef并不比在类外typedef更破坏封装性。它会违反"封装"的哪种含义呢?你没有透露有关Foo实现的任何信息。你只是定义了一个类型。这与Foo*和Foo之间的关系完全类似。Foo*是指向Foo的某种指针(恰好是默认类型)。Foo::p是指向Foo的另一种指针。你没有破坏封装性,只是增加了类型系统。


4

我曾经使用过外部和封装的typedef,但最终选择了前者。

typedef boost::shared_ptr<Foo> FooPtr; 

这样做仅仅是因为在组合表达式中,这样看起来比Foo::Ptr更清晰。

你不觉得现在Foo已经“意识到”它将如何传递吗?

通常,这些类只能通过工厂方法创建:

struct Foo
{
     static FooPtr Create() { return FooPtr(new Foo); }

   protected:
     Foo() {}
}

这种方式比封装typedef更"强大",但是却是一种非常常见的模式。


2

我不是匈牙利命名法的粉丝,通常我使用:

typedef boost::shared_ptr<Foo> FooSharedPtr;

足够详细以清晰表达,但又不至于过于繁琐。无论如何,我一定会明确指出这是一个共享指针,特别是如果未来还有其他人要使用该类型。


2

我通常将typedef封装在类内部。原因是我们有一些内存敏感的代码,这样可以轻松地在boost::shared_ptrboost::intrusive_ptr之间切换。 由于intrusive_ptr是类需要支持的内容,所以我认为将使用哪种共享指针的知识包装在类中是有意义的。


1
  typedef shared_ptr<Foo> sptr_Foo;
  typedef weak_ptr<Foo>   wptr_Foo;
  typedef unique_ptr<Foo> uptr_Foo;

1
怎么样:
template <typename T>
class Shared
{
    public: 
        typedef std::shared_ptr<T> Ptr; // or boost::shared_ptr if you will
};

然后允许任何共享类都拥有自己的Ptr对象,例如:

class myClass : public Shared<myClass>
{
};

int main()
{
    myClass::Ptr object;
    //...
    object->foo();
}

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