何时使用指针,何时不使用?

29

我习惯于使用Java进行编程,在Java中你几乎不需要考虑指针。但目前我正在写C++程序。在创建包含其他类成员的类时,何时应该使用指针,何时不应该使用?例如,什么时候会想要这样做:

class Foo {
    Bar b;
}

与此相反:

class Foo {
    Bar* b;
}
5个回答

42

避免使用指针。

以下情况下可以使用它们:

  • 你想要使用Pimpl idiom,或者抽象工厂模式。
  • Bar 实例实际上由程序的其他部分管理,而 Foo 类只需要能够访问它。
  • 你希望推迟构造 Bar 对象(也就是说,你希望在构造 Foo 之后创建它)。
  • 在你的业务逻辑中,Bar 对象可能根本不存在;在 Java 中,你将使用 null。但是,还可以看看boost::optional
  • Bar 实际上是一个基类,而且你需要该实例是多态的。
  • 你恰好正在使用一种工具包,它倾向于将 GUI 小部件表示为指针。例如(但不限于)wxWidgetsGLUI

在任何这些情况 (*)下,从使用智能指针开始,例如 boost::shared_ptr。否则,你很可能会忘记释放内存,迟早会出问题。一旦你知道你在做什么,请逐案考虑什么类型的指针最好。

(*) 任何情况 - 除了可能与 GUI 小部件相关的项目;在这种情况下,你的工具包很可能也会为你管理资源。


如果你正在编写工具包(这通常是我的情况),那么你可以选择使用智能指针来解决指针问题。我从未遇到过智能指针无法解决的指针问题,因此我不会说“一定要用它们!”,但是在大多数情况下,“使用智能指针是最容易的方法”。 - strager
也许只有我一个人这么认为,但我认为特别是初学者应该(当然不要在实际项目中)先尝试使用原始指针。我认为这类似于“IDE / 没有IDE”的问题。他们应该学习了解智能指针何时有用以及它们避免哪些陷阱。 - Johannes Schaub - litb
@litb:我同意你的观点。如果你问的是关于实际项目的最佳实践(这是我回答的问题),或者学习理解基本知识,答案可能会有所不同。另一方面,我认为他已经知道如何在基本情况下使用原始指针了。 - Reunanen
1
起点应该是 boost::scoped_ptr,只有在需要那种功能时才使用 shared_ptr。 - Patrick
如果您正在使用Qt进行编程,请使用QPointer<T>作为智能指针。 - Nathan Moos
显示剩余2条评论

19
class Foo {
    Bar b;
}

b被包含在Foo中。如果Foo对象结束生命周期,b也会自动结束生命周期。这就是组合的模型。上面的b表示对象本身,而不仅仅是像Java中的指针。因此,如果b超出作用域,对象也将结束生命周期。

class Foo {
    Bar * b;
}

这里,对象b指向的是Foo对象使用或引用的内容。如果Foo对象结束其生命周期,b指向的对象可能会继续存在,具体取决于情况。这可以用来模拟聚合和一般关系。例如,该对象可以与其他Foo对象共享。

指针在C++中与Java中的引用类似。它们也可以指向空值。如果一个指针指向空值,则称为空指针。

与指针类似的是引用。在C++中,引用必须被初始化,并且只能指向一个(有效的)对象,对于该引用被初始化时所指的对象而言。因此,引用不能持有可表示“空值”的值,如Java中的null


1
这可能有点晚了,但在第一段中,你不应该说“如果Foo对象结束生命周期,b自动结束生命周期”,而不是在Foo的位置上使用Bar - His
很好的答案。第一种情况(组合)是在Bar依赖于Foo时使用的(汽车的轮子是汽车的一部分),而第二种情况(聚合)是当Bar独立于Foo时使用的(驾驶汽车的人不是汽车的一部分)。 - Abdel Aleem

3

在第一个例子中,当你构造Foo对象时,Bar对象的内存将会自动分配。而在第二个例子中,你需要手动分配内存。因此,你必须调用:

Foo *foo = new Foo();
foo->b = new Bar();

如果Bar对象很大,您不想将其与Foo对象捆绑在一起,那么这种方法可能是可取的。当Bar对象的构建与Foo对象的创建无关时,这也是可取的。在这种情况下,b对象被“注入”到foo中:

Foo *foo = new Foo();
foo->b = b_ptr;

在某个地方构造了b_ptr,并将指针传递给foo。

对于较小的对象来说,这是很危险的,因为你可能会忘记分配内存。


new 返回一个指针,而不是一个值。你是想用 Foo foo; 还是 Foo foo = Foo(); 代替? - strager

1

在不同的情况下,两者都可以。例如,如果您知道如何在构造类Foo的对象时构造b,则第一种方法是可行的。但是,如果您不知道,唯一的选择就是使用第二种方法。


1
你需要进行一些汇编语言编程,并且要深入了解内存布局。C语言只是一种跨平台的汇编语言,不像Java或其他语言。要正确地使用它,应该理解底层细节。
所有发表的评论都是有效的,但对于像你这样从高级语言转到C语言的人来说,拥有这种经验将会更加有益。通过透彻的理解,你不再会问那样的问题了。

1
请注意,这个问题是关于C++而不是C的。 - Reunanen
但是新手不知道C++和C之间的区别。这仍然是一个很好的建议。 - toto

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