数组:存储对象或引用

时间:2011-11-09 17:24:16

标签: c++

作为Java开发人员,我有以下C ++问题。

如果我有类型A的对象,并且我想将它们的集合存储在数组中, 那么我应该只存储指向对象的指针还是存储对象本身更好?

在我看来,最好存储指针,因为: 1)通过将其指针设置为null,可以轻松删除对象 2)节省空间。

6 个答案:

答案 0 :(得分:7)

指针还是只是对象?

您无法在C ++中将引用放入数组中。你可以创建一个指针数组,但我仍然更喜欢容器和实际对象而不是指针,因为:

  1. 没有机会泄漏,异常安全更容易处理。
  2. 空间不小 - 如果你存储一个指针数组,你需要对象的内存加上指针的内存。
  3. 我主张在一个容器(或数组,如果你必须)中放置指针(或智能指针会更好)的唯一时候是你的对象不是可复制构造和可分配的(对容器的要求,指针总是满足这个)或者你需要它们是多态的。 E.g。

    #include <vector>
    
    struct foo {
      virtual void it() {}
    };
    
    struct bar : public foo {
      int a;
      virtual void it() {}
    }; 
    
    int main() {
      std::vector<foo> v;
      v.push_back(bar()); // not doing what you expected! (the temporary bar gets "made into" a foo before storing as a foo and your vector doesn't get a bar added)
      std::vector<foo*> v2;
      v2.push_back(new bar()); // Fine
    }
    

    如果你想走这条路boost pointer containers可能会引起人们的兴趣,因为他们为你做了所有艰苦的工作。

    从数组或容器中删除。

    分配NULL不会导致容器/数组中的指针变少,(它也不会处理delete),大小保持不变但现在有指针你不能合法取消引用。这使得代码的其余部分以额外的if语句的形式变得更加复杂,并禁止诸如以下内容:

    // need to go out of our way to make sure there's no NULL here
    std::for_each(v2.begin(),v2.end(), std::mem_fun(&foo::it));
    

    我真的不喜欢在一般指针序列中允许NULL的想法,因为你很快就会把所有实际工作都埋没在一系列条件语句中。另一种方法是std::vector提供一个erase方法,它接受一个迭代器,所以你可以写:

    v2.erase(v2.begin());
    

    删除第一个或v2.begin()+1为第二个。由于时间的复杂性,在std::vector上没有简单的“擦除第n个元素”方法 - 如果你进行了大量的擦除,那么还有其他容器可能更合适。

    对于阵列,您可以使用以下方法模拟擦除:

    #include <utility>
    #include <iterator>
    #include <algorithm>
    #include <iostream>
    
    int main() {
      int arr[] = {1,2,3,4};
      int len = sizeof(arr)/sizeof(*arr);
      std::copy(arr, arr+len, std::ostream_iterator<int>(std::cout, " "));
      std::cout << std::endl;
    
      // remove 2nd element, without preserving order:
      std::swap(arr[1], arr[len-1]);
      len -= 1;
      std::copy(arr, arr+len, std::ostream_iterator<int>(std::cout, " "));
      std::cout << std::endl;
    
      // and again, first element:
      std::swap(arr[0], arr[len-1]);
      len -= 1;
      std::copy(arr, arr+len, std::ostream_iterator<int>(std::cout, " "));
      std::cout << std::endl;
    }
    

    保留顺序需要一系列shuffle而不是单个swap,这很好地说明了std::vector面临的擦除的复杂性。当然,通过这样做,你只需要重新创造一个相当大的轮子,而不是标准库容器为你免费提供的那么有用和灵活!

答案 1 :(得分:1)

听起来你的指针令人困惑。 C ++有3种常用的表示对象句柄的方法

  • 参考
  • 指针

来自Java,最类似的方法是使用指针。这可能是你想要在这里做的。

如何存储它们会对其行为产生一些非常重要的影响。当您存储为值时,您经常处理值的副本。指针处理具有多个引用的一个对象。如果没有关于这些对象的更多背景,那么给出一个平坦的答案比另一个更好是不可能的

答案 2 :(得分:1)

这完全取决于你想做什么......但你在某些方面被误导了。

你应该知道的事情是:

  1. 您不能在C ++中设置对NULL的引用,但您可以将指针设置为NULL。
  2. 只能对现有对象进行引用 - 它必须按原样初始化。
  3. 无法更改引用(尽管引用的值可以是)。
  4. 你不会节省空间,事实上你会使用更多,因为你正在使用一个对象和一个引用。如果你需要多次引用同一个对象,那么你可以节省空间,但是你也可以使用一个指针 - 它在MOST(阅读:并非所有)场景中更灵活。
  5. 最后一个重要的一个:STL容器(向量,列表等)具有COPY语义 - 它们不能与引用一起使用。它们可以使用指针,但它变得复杂,所以现在你应该总是在这些容器中使用可复制对象并接受它们将被复制,无论它与否。 STL旨在通过复制语义高效安全。
  6. 希望有所帮助! :)

    PS(编辑):您可以在BOOST使用一些新的功能/ TR1(谷歌他们),使容器/ shared_ptr的数组(引用计数智能指针),它会给你一个类似的感觉Java的引用和垃圾收集。有一系列的差异,但你必须自己阅读 - 它们是新标准的一大特色。

答案 3 :(得分:1)

您应该尽可能存储对象;这样,容器将为您管理对象的生命周期。

偶尔,您需要存储指针;最常见的是指向基类的指针,其中对象本身将是不同类型的。在这种情况下,您需要小心自己管理对象的生命周期;确保它们在容器中不被破坏,但一旦不再需要它们就会被销毁。

与Java不同,将指针设置为null会使解除分配指向的对象;相反,如果没有更多指向对象的指针,则会出现内存泄漏。如果使用new创建对象,则必须在某个时刻调用delete。这里你最好的选择是存储智能指针(shared_ptr,或者unique_ptr如果可用),或使用Boost的指针容器。

答案 4 :(得分:0)

您无法在容器中存储引用。你可以存储(裸)指针,但这很容易出错,因此不赞成。

因此,真正的选择是在存储对象和智能指针之间进行对象。两者都有它们的用途。我的建议是按值存储对象,除非特殊情况另有要求。这可能发生:

  • 如果您需要将对象清空而不从中删除它 容器;
  • 如果你需要存储指向同一个对象的指针 多个容器;
  • 如果您需要处理容器的元素 多晶型。

这样做的一个原因是节省空间,因为按值存储元素可能更节省空间。

答案 5 :(得分:0)

添加到aix的答案:

如果要存储多态对象,则必须使用智能指针,因为容器会复制,而对于派生类型,只复制基本部分(至少是标准部分,我认为boost有一些不同的容器)。因此,您将丢失对象的任何多态行为(以及任何派生类状态)。