数组:存储对象或引用

Array: Storing Objects or References

本文关键字:引用 对象 存储 数组      更新时间:2023-10-16

作为一名Java开发人员,我有以下c++问题:

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

在我看来,最好存储指针,因为:1)通过将对象的指针设置为空,可以很容易地删除对象2)节省空间。

指针还是对象?

在c++中不能在数组中放置引用。您可以创建指针数组,但我仍然更喜欢容器和实际对象,而不是指针,因为:

  1. 没有机会泄漏,异常安全更容易处理。
  2. 不是更少的空间——如果你存储一个指针数组,你需要对象的内存加上指针的内存。

我提倡将指针(或智能指针将更好)放在容器(或数组,如果你必须)的唯一时间是当你的对象不是复制可解释和可赋值(容器的要求,指针总是满足这一点),或者你需要他们是多态的。例如

#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指针容器可能会让你感兴趣,因为它们为你做了所有艰苦的工作。

从数组或容器中移除。

赋值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;
}

保持顺序需要一系列的洗牌而不是一次交换,这很好地说明了擦除std::vector所面临的复杂性。当然,通过这样做,您只是重新发明了一个相当大的轮子,比免费的标准库容器有用和灵活得多!

听起来你把引用和指针搞混了。c++有三种常见的表示对象句柄的方法

    引用
  • <
  • 指针/gh>
来自Java的最类似的方法是使用指针。这可能就是你在这里想要做的。

它们的存储方式对它们的行为有一些非常基本的影响。当您将其存储为值时,您通常会处理值的副本。指针用多个引用处理一个对象。如果没有更多关于这些对象的作用的上下文,那么给出一个简单的答案是不可能的

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

你应该知道的是:

  1. 在c++中不能将引用设置为NULL,但可以将指针设置为NULL。
  2. 一个引用只能指向一个已经存在的对象——它必须初始化。
  3. 不能更改引用(但可以更改引用的值)。
  4. 你不会节省空间,事实上你会使用更多,因为你使用一个对象和一个引用。如果你需要多次引用同一个对象,那么你可以节省空间,但你也可以使用指针-它在大多数(读:不是所有)情况下更灵活。
  5. 最后一个重要的:STL容器(vector, list等)具有COPY语义-它们不能与引用一起工作。它们可以与指针一起工作,但它会变得复杂,所以现在你应该总是在这些容器中使用可复制对象,并接受它们将被复制的事实,不管你喜欢与否。STL被设计为高效和安全的复制语义。

希望有帮助!:)

PS(编辑):你可以使用BOOST/TR1中的一些新特性,并创建一个shared_ptr(引用计数智能指针)的容器/数组,这将给你类似于Java的引用和垃圾收集的感觉。有许多不同之处,但你必须自己阅读-它们是新标准的一个重要功能。

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

有时需要存储指针;最常见的是,指向基类的指针,其中对象本身具有不同的类型。在这种情况下,您需要自己小心地管理对象的生命周期;确保它们在容器中不被销毁,而是在不再需要时被销毁。

与Java不同,将指针设置为null不会使 释放所指向的对象;相反,如果不再有指向该对象的指针,就会发生内存泄漏。如果对象是使用new创建的,那么必须在某个时候调用delete。这里最好的选择是存储智能指针(shared_ptr,或者unique_ptr,如果可用),或者使用Boost的指针容器。

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

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

  • 如果您需要清空对象而不将其从容器;
  • ,如果需要将指向同一对象的指针存储在多个容器;
  • 如果你需要处理容器的元素以多态方式。

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

添加aix的答案:

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