指向对象的引用

3
解引用指针会使代码非常难以阅读。我通常的做法是将指向对象的引用放入,并使用该引用进行操作。例如:
shared_ptr<std::vector<int> > sp = get_sp_to_vector();
std::vector<int>& vec = *sp;
...
vec.push_back(5);

我想知道这是否是一种好的做法。它有什么缺点吗?
更新:为了完成示例,我将以下列方式定义get_sp_to_vector()函数:
shared_ptr<std::vector<int> >  get_sp_to_vector()
{
    // create a vector and send back a shared pointer pointing at it
    shared_ptr<std::vector<int> >  sp(new std::vector<int>);
    sp->push_back(1);  sp->push_back(3);
    return sp;
}

2
我也这样做。当我有某种指针但实际想要处理的是它所指向的东西时,这似乎对我来说很合理。为了避免不同类型的变量之间在同一作用域中引用相同值的名称冲突,我使用像“foo”这样的名称表示引用或实际对象,“p_foo”表示指针,“ap_foo”表示std::auto_ptr,而“sp_foo”则表示boost::shared_ptrstd::tr1::shared_ptr - Wyzard
最近似乎有一种趋势,即在评论中回答问题... 一群懦夫。勇敢点。如果可能的话,我会给那个评论点个踩 - 不是因为我认为答案不好,而是因为它是一个答案,因此不是一个评论 - oz10
1
@ceretullis:不,我的评论并没有回答它是否有任何缺点的问题。这就是为什么我选择写评论而不是答案的原因。 - Wyzard
@Wyzard:「我在想这是否是一个好的做法」实际上是一个伪装成陈述的问题,而你已经明确回答了这个问题。 - oz10
5个回答

2
我不认为这是一个好的实践。指针是C/C++开发的主要支柱,因此C/C++编程人员熟悉指针所需的解引用语法。我尽量避免使用指针(并非出于语法原因),但有时它确实是最好的工具。如果您的代码因为使用指针而变得复杂难懂,我通常会将其解引用到通过引用传递对象的函数中,这本质上做了你正在做的事情,但在我看来更加优雅。因此,不再是:
shared_ptr<std::vector<int> > sp = get_sp_to_vector();
std::vector<int>& vec = *sp;
...ugly stuff...
vec.push_back(5);

我会这样做:

void func(std::vector<int> &vec)
{
     ... previously ugly stuff...
     vec.push_back(5);
}

shared_ptr<std::vector<int> > sp = get_sp_to_vector();
func(*sp);

编辑:
这并不是说你应该创建一个函数只是为了创造更好的指针语法,而是表明如果你不喜欢指针语法,而你的代码使用简洁的函数,你可以简单地使函数接受引用并在调用函数时取消引用指针。对于仅有少量*p或p->x调用的情况,创建一个带有引用的函数或创建一个引用以调用p.x似乎很愚蠢。在这些情况下,只需使用指针语法,因为这是C/C++语法。

其他人提出在循环中使用引用,其中指针可能需要多次取消引用。我同意在这些情况下使用引用会很有益。


3
@RC:你正在做完全相同的事情……只是将它隐藏在一个函数调用中(根据要执行的操作情况,这是合适的)。 - oz10
是的,这正是我在回答中所说的。我发现将解引用指针作为引用传递给函数与在同一代码块中创建引用不同。我的意思不是说我会编写一个函数仅仅为了使代码在语法上看起来不同。我的观点是,如果你不喜欢指针,就不要将它们作为指针传递给函数调用。 - RC.
@RC:好的...我同意你的看法,但是你的回答中并没有表达得很清楚。也许只是我的理解有问题,但是你似乎建议Jabba进行一种提取方法类型的重构,仅仅是为了隐藏引用的创建过程。 - oz10

2

使用本地引用是很常见的,特别是在循环体内部,但有一个要求:只有当引用明显会存活与目标对象一样长时才这样做。

通常情况下,这并不难保证,但以下是一些不好的例子:

shared_ptr<X> p = get_p();
X& r = *p;
p.reset(); // might result in destroying r
use(r); // oops

// or:
shared_ptr<X> p = get_p();
X& r = *p;
p = get_some_other_p(); // might result in destroying r
use(r); // oops

shared_ptr没有release()函数,只有reset()函数,但我同意这一点——将两个变量耦合在一起会增加复杂度的非零开销。由于其中一个是引用,你甚至不能更新r以匹配p的新值。 - Steve Jessop
哎呀,谢谢,正如你所认识到的那样,意图才是最重要的。 - Roger Pate

2

在我看来,我认为这是一个可以接受的做法(有一些注意事项-请参见Roger Pate的文章)。如果您只是为push_back()调用添加一行代码,那么我认为这是不可接受的。

然而,如果您发现您正在对指针进行许多次解引用操作,那么不仅将其解引用成一个引用对象是可以接受的,它还可能提高性能(取决于您的编译器、相关代码、月相等因素)。


如果经常解引用指针,可以提出一个很好的使用案例。我想知道编译器在检测到解引用指针的循环时会多久为您执行此优化?似乎需要/希望知道循环将被执行多少次,以便知道是否有益。 - RC.
2
实际上,将指针解引用为引用在几乎所有编译器中都对性能没有任何影响。在底层,引用基本上是以指针相同的方式实现的。 - Terry Mahaffey
同意 Terry Mahaffey 的观点。然而,使用数组中特定条目的引用可能会节省重复添加的操作。 - rlbond
@"Terry Mahaffey": 我同意。引用在底层实现上与指针完全相同,如果您使用引用几次,就不可能获得任何性能提升。我还发现,在某些情况下,指向结构体的指针会导致编译器在访问每个成员时都解引用指针... 通过将指针解引用一次并使用引用,我能够改善编译器生成的汇编代码。你觉得这合理吗? - oz10

1

从你提出的代码反推回来,我不同意:

shared_ptr<std::vector<int> > sp = get_sp_to_vector();
...
sp->push_back(5);

这段代码“非常难读”。它也比你的代码短。所以虽然我认为定义和使用本地引用没有什么问题,但我也不认为你应该制定一个总是这样做的规则。

也许如果你的指针代码很难读,那是因为你缺少一些技巧,可以使它更简单。也许如果你提出另一个问题,附上一个你目前需要用引用修复的坏指针代码示例,SOers会找到替代方案。


1
如果我实际上正在进行指针操作,那么我会将它保留为指针。 如果我的指针只是一个可空引用,那么我会将它保留为指针。 如果我确信或希望确保指针始终非零 (即如果我从方法中返回非nil对象),那么 我通过使用引用来明确表示这一点。

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