作为Java开发人员,我有以下C ++问题。
如果我有类型A的对象,并且我想将它们的集合存储在数组中, 那么我应该只存储指向对象的指针还是存储对象本身更好?
在我看来,最好存储指针,因为: 1)通过将其指针设置为null,可以轻松删除对象 2)节省空间。
答案 0 :(得分:7)
您无法在C ++中将引用放入数组中。你可以创建一个指针数组,但我仍然更喜欢容器和实际对象而不是指针,因为:
我主张在一个容器(或数组,如果你必须)中放置指针(或智能指针会更好)的唯一时候是你的对象不是可复制构造和可分配的(对容器的要求,指针总是满足这个)或者你需要它们是多态的。 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)
这完全取决于你想做什么......但你在某些方面被误导了。
你应该知道的事情是:
希望有所帮助! :)
PS(编辑):您可以在BOOST使用一些新的功能/ TR1(谷歌他们),使容器/ shared_ptr的数组(引用计数智能指针),它会给你一个类似的感觉Java的引用和垃圾收集。有一系列的差异,但你必须自己阅读 - 它们是新标准的一大特色。
答案 3 :(得分:1)
您应该尽可能存储对象;这样,容器将为您管理对象的生命周期。
偶尔,您需要存储指针;最常见的是指向基类的指针,其中对象本身将是不同类型的。在这种情况下,您需要小心自己管理对象的生命周期;确保它们在容器中不被破坏,但一旦不再需要它们就会被销毁。
与Java不同,将指针设置为null会使不解除分配指向的对象;相反,如果没有更多指向对象的指针,则会出现内存泄漏。如果使用new
创建对象,则必须在某个时刻调用delete
。这里你最好的选择是存储智能指针(shared_ptr
,或者unique_ptr
如果可用),或使用Boost的指针容器。
答案 4 :(得分:0)
您无法在容器中存储引用。你可以存储(裸)指针,但这很容易出错,因此不赞成。
因此,真正的选择是在存储对象和智能指针之间进行对象。两者都有它们的用途。我的建议是按值存储对象,除非特殊情况另有要求。这可能发生:
不这样做的一个原因是节省空间,因为按值存储元素可能更节省空间。
答案 5 :(得分:0)
添加到aix的答案:
如果要存储多态对象,则必须使用智能指针,因为容器会复制,而对于派生类型,只复制基本部分(至少是标准部分,我认为boost有一些不同的容器)。因此,您将丢失对象的任何多态行为(以及任何派生类状态)。