【问题标题】:c++: when to use pointers?c++:什么时候使用指针?
【发布时间】:2010-10-14 01:48:06
【问题描述】:

在阅读了一些教程后,我得出结论,应该始终使用指针来表示对象。但是我在阅读一些 QT 教程 (http://zetcode.com/gui/qt4/painting/) 时也看到了一些例外,其中 QPaint 对象是在堆栈上创建的。所以现在我很困惑。什么时候应该使用指针?

【问题讨论】:

  • QT 教程在堆栈上创建 QPaint 对象,因为 QPaint 对象的使用寿命在编译时是已知的,并且完全由一对匹配的 {} 限定。
  • 同样适用于 QPen、QPainter 和 QApplication。
  • 关于指针的好消息是,我们现在在 C++ 中有一些安全的指针实现,例如 unique_ptr 或 weak_ptr,当你需要使用指针时,你需要使用它们(有一些罕见的例外低级代码)

标签: c++ qt pointers object


【解决方案1】:

如果可能,永远不要使用指针。依靠引用传递,或者如果你要返回一个结构或类,假设你的编译器有返回值优化。 (但是,您必须避免对返回的类进行条件构造)。

Java 没有指针是有原因的。 C++ 也不需要它们。如果您避免使用它们,您将在对象离开范围时获得自动对象销毁的额外好处。否则,您的代码将生成各种类型的内存错误。由于未处理的异常,内存泄漏可能很难找到,并且经常在 C++ 中发生。

如果您必须使用指针,请考虑一些智能指针类,例如 auto_ptr。对象的自动销毁不仅仅是释放底层内存。有一个概念叫做 RAII。一些对象需要额外处理销毁。例如互斥锁和关闭文件等。

【讨论】:

  • 嗯,Java 没有“有”指针,因为除了原语之外的所有东西都是隐含的指针。
【解决方案2】:

说到 C++,当程序离开创建它的作用域时,在堆栈上创建的对象不能使用。所以一般来说,当你知道不需要通过函数或右大括号的变量时,你可以在堆栈上创建它。

特别谈到 Qt,Qt 通过处理堆对象的大量内存管理来帮助程序员。对于派生自 QObject 的对象(几乎所有以“Q”为前缀的类都是),构造函数采用可选参数 parent。然后父对象拥有该对象,当父对象被删除时,所有拥有的对象也将被删除。本质上,子对象的销毁责任被传递给了父对象。使用这种机制时,必须在堆上创建子QObjects。

简而言之,在 Qt 中,您可以轻松地在堆上创建对象,并且只要您设置了正确的父对象,您只需要担心破坏父对象。但是,在一般 C++ 中,您需要记住销毁堆对象或使用智能指针。

【讨论】:

    【解决方案3】:

    使用行为良好的可复制对象按值传递是处理大量代码的方法。

    如果速度真的很重要,请尽可能使用按引用传递,最后使用指针。

    【讨论】:

      【解决方案4】:

      使用指针与两个正交的东西有关:

      1. 动态分配。通常,当对象的寿命要长于创建它的范围时,您应该动态分配。这样的对象是必须明确指定所有者的资源(最常见的是某种智能指针)。

      2. 按地址访问(无论对象是如何创建的)。在这种情况下,指针并不意味着所有权。在以下情况下可能需要此类访问:

        • 一些已经存在的界面需要这样做。
        • 应该对可能为空的关联进行建模。
        • 应避免复制大对象,否则根本无法复制,但不能使用引用(例如 stl 集合)。

      #1 和#2 可以出现在不同的配置中,例如您可以想象通过指针访问动态分配的对象,但这样的对象也可以通过引用传递给某些函数。您还可以获得指向在堆栈上创建的某个对象的指针等。

      【讨论】:

      • “两个正交的东西”——终于有人说出来了! c.f. 我要补充一点 2 点,即通过引用/指针(=地址)访问会启用多态性,而不管对象是如何分配的——这与我看到的令人困惑的帖子数量相反,这意味着一个有效的动态分配的原因是授予多态行为 - 这不是必需的,不是参数,并且完全不相关。
      【解决方案5】:

      使用指针的主要原因:

      1. 控制对象生命周期;
      2. 不能使用引用(例如,您想在向量中存储不可复制的内容);
      3. 您应该将指针传递给某个第三方函数;
      4. 可能是一些优化原因,但我不确定。

      【讨论】:

      • 我认为指针最重要的一点是控制对象的生命周期
      • 第 2 点特别适用于 Qt,因为大多数与 Qt 图形相关的对象是不可复制的。
      • @caparcode,实际上,QObjects 是不可复制的。这包括 GUI 元素和很少的图形类,以及其他一些东西。无论出于何种原因,其他类也可能是不可复制的。
      • 引用可以存储在向量中:std::vector >
      【解决方案6】:

      通常在以下情况下必须使用指针:

      1. 您需要一个属于不同类的对象集合(在大多数情况下,它们有一个共同的基类)。

      2. 您需要一个堆栈分配的对象集合,该集合非常大,可能会导致堆栈溢出。

      3. 您需要一种可以快速重新排列对象的数据结构 - 如链表、树等。

      4. 您需要一些复杂的对象生命周期管理逻辑。

      5. 您需要一个允许从对象直接导航到对象的数据结构 - 例如链表、树或任何其他图表。

      【讨论】:

      • 在 (1) 的情况下,没有“可能”需要一个共同的基础。在 (2) 的情况下,唯一适用的“集合”将是 C 样式的数组。
      • 对于情况 (1) 没有必要,您可能希望存储一个 void* 数组以便能够调用 free() - 这不是 C++ 的做事方式,但可能是。对于案例 (2),我的意思是指针数组与实际对象数组。如果对象真的很大,存储指针会节省很多堆栈空间。
      【解决方案7】:

      那些教程是什么?实际上,规则是只有在绝对必要时才应该使用指针,这种情况很少见。你需要阅读一本关于 C++ 的好书,比如 Koenig & Moo 的 Accelerated C++

      编辑: 澄清一点 - 您将 使用指针的两个实例(此处使用字符串作为示例 - 任何其他类型也一样):

      class Person {
         public:
            string name;       // NOT string * name;
         ...
      };
      
      
      void f() {
         string value;        // NOT string * value
         // use vvalue
      }
      

      【讨论】:

      • stack的局限性如何?
      • 会有什么限制?
      • 好吧,我的意思是在某些系统上,如果不是所有的堆栈大小都受到限制,不是吗?
      • @presario:堆栈大小是有限的,但出于正常原因,您不必在这种情况下使用指针
      • 指针是一种工具。将它们的使用限制为“仅在您绝对必须时”过于严格。通过适当的设计考虑,它们可以成为非常可维护和健壮的设计的关键。
      【解决方案8】:

      首先,问题是错误的:困境不在指针和堆栈之间,而是在堆和堆栈之间。您可以在堆栈上有一个对象并将指针传递给该对象。我假设您真正要问的是您是否应该声明指向类的指针或类的实例。

      答案是这取决于你想对对象做什么。如果控件离开函数后对象必须存在,那么您必须使用指针并在堆上创建对象。例如,当您的函数必须返回指向已创建对象的指针或将对象添加到在调用您的函数之前创建的列表时,您将执行此操作。

      另一方面,如果对象是函数的本地对象,那么最好在堆栈上使用它。这使编译器能够在控件离开函数时调用析构函数。

      【讨论】:

        【解决方案9】:

        我不清楚您的问题是 ptr-to-obj vs stack-based-obj 还是 ptr-to-obj vs reference-to-obj。还有一些用途不属于任何一类。

        关于 vs stack,这似乎已经在上面介绍过了。几个原因,最明显的是对象的生命周期。

        关于 vs 引用,总是努力使用引用,但是有些事情你只能用 ptrs 做,例如(有很多用途):

        • 遍历数组中的元素(例如,遍历标准数组 [])
        • 当被调用的函数分配一些东西并通过 ptr 返回它时

        最重要的是,指针(和引用,相对于自动/基于堆栈的静态对象)支持多态性。指向基类的指针实际上可能指向派生类。这是 C++ 支持的 OO 行为的基础。

        【讨论】:

        • +1 表示最后一个加粗的段落,因为我自己不能再投票了。
        • 但是您可以在堆栈上分配一些东西,然后通过将引用或指针传递给您的函数/容器/其他东西来多态地使用它。 存储持续时间和多态性是正交概念。 然而,人们继续回答问题,即为什么人们可以通过引用多态性作为优势来通过指针进行分配,而没有解释为什么两者相关(它们不相关)。
        【解决方案10】:

        我实际上在这种情况下使用指针:

        class Foo
        {
            Bar* bar;
        
            Foo(Bar& bar) : bar(&bar) { }
        
            Bar& Bar() const { return *bar; }
        };
        

        在此之前,我使用了从构造函数初始化的引用成员,但是编译器在创建复制构造函数、赋值运算符等时遇到了问题。

        戴夫

        【讨论】:

          【解决方案11】:

          除了其他人提出的观点(尤其是控制对象生命周期),如果您需要处理 NULL 对象,您应该使用指针,而不是引用。可以通过类型转换创建 NULL 引用,但这通常是个坏主意。

          【讨论】:

          • 这实际上是一个非常可怕的想法。
          【解决方案12】:

          如果您不知道何时应该使用指针,请不要使用它们。

          当您需要使用它们时会变得很明显,每种情况都不同。何时应该使用它们并不容易简明总结。不要养成“总是使用指针指向对象”的习惯,这肯定是个坏建议。

          【讨论】:

          • 这不是答案。该网站的目的是帮助人们了解更多信息,而不是在您无法表达自己的想法时忽略他们。
          • @A.R.希望我确实让 jamolkhon 学会了不要总是使用指针——看看他问题的第一句话。我考虑了这个背景,而不仅仅是标题。此外,其他问题列出了一些用途。
          • @A.R.我也是。但是考虑上下文可以帮助您创建一个比仅查看包含问题的句子更有用的答案 - 特别是如果其他人已经解决了这个问题。
          • @A.R.没必要刻薄。其他人觉得我的回答很有帮助,如果你不这样做也没关系,只需投反对票并继续前进。
          • @A.R. OP 得出的​​结论是他应该始终使用指针,我觉得我的回答对这个具体案例很有帮助。我已经多次看到这种情况,即使在人们在不需要时使用指针的专业代码库中也是如此。我的建议是不要总是使用它们,对于确实需要它们的情况,它会变得很明显。它不能适用于所有问题——也许只有那些人们得出结论认为应该始终使用实际上可能有害的东西的问题。
          【解决方案13】:

          当你不希望你的对象在堆栈帧被清空时被销毁时使用指针。

          尽可能使用引用传递参数。

          【讨论】:

          • int x; int* pointer_to_x = &x;您在重复 OP 在指针和堆分配之间的混淆。
          • 不,通过 const 引用而不是值传递是一个好习惯。虽然它在实现级别使用指针,但在概念上与它们无关。
          【解决方案14】:

          一般在以下情况下使用指针/引用对象:

          • 将它们传递给其他方法

          • 创建一个大数组(我不确定正常的堆栈大小是多少)

          在以下情况下使用堆栈:

          • 您正在创建一个在方法中存在和死亡的对象

          • 对象是 CPU 寄存器的大小或更小

          【讨论】:

          • 那么你们俩:在堆栈上传递对象,在堆栈​​上创建大数组,在堆上分配局部变量,在堆上分配整数?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-10-31
          • 2012-02-01
          • 2010-12-06
          • 1970-01-01
          相关资源
          最近更新 更多