数组:存储对象或引用
Array: Storing Objects or References
作为一名Java开发人员,我有以下c++问题:
如果我有A类型的对象,我想把它们的集合存储在一个数组中,那么我应该存储指向对象的指针还是存储对象本身更好?
在我看来,最好存储指针,因为:1)通过将对象的指针设置为空,可以很容易地删除对象2)节省空间。
指针还是对象?
在c++中不能在数组中放置引用。您可以创建指针数组,但我仍然更喜欢容器和实际对象,而不是指针,因为:
- 没有机会泄漏,异常安全更容易处理。
- 不是更少的空间——如果你存储一个指针数组,你需要对象的内存加上指针的内存。
我提倡将指针(或智能指针将更好)放在容器(或数组,如果你必须)的唯一时间是当你的对象不是复制可解释和可赋值(容器的要求,指针总是满足这一点),或者你需要他们是多态的。例如
#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>
它们的存储方式对它们的行为有一些非常基本的影响。当您将其存储为值时,您通常会处理值的副本。指针用多个引用处理一个对象。如果没有更多关于这些对象的作用的上下文,那么给出一个简单的答案是不可能的
这完全取决于你想做什么…但你在某些方面被误导了。
你应该知道的是:
- 在c++中不能将引用设置为NULL,但可以将指针设置为NULL。
- 一个引用只能指向一个已经存在的对象——它必须初始化。
- 不能更改引用(但可以更改引用的值)。
- 你不会节省空间,事实上你会使用更多,因为你使用一个对象和一个引用。如果你需要多次引用同一个对象,那么你可以节省空间,但你也可以使用指针-它在大多数(读:不是所有)情况下更灵活。
- 最后一个重要的:STL容器(vector, list等)具有COPY语义-它们不能与引用一起工作。它们可以与指针一起工作,但它会变得复杂,所以现在你应该总是在这些容器中使用可复制对象,并接受它们将被复制的事实,不管你喜欢与否。STL被设计为高效和安全的复制语义。
希望有帮助!:)
PS(编辑):你可以使用BOOST/TR1中的一些新特性,并创建一个shared_ptr(引用计数智能指针)的容器/数组,这将给你类似于Java的引用和垃圾收集的感觉。有许多不同之处,但你必须自己阅读-它们是新标准的一个重要功能。
你应该尽可能地存储对象;这样,容器将为您管理对象的生命周期。
有时需要存储指针;最常见的是,指向基类的指针,其中对象本身具有不同的类型。在这种情况下,您需要自己小心地管理对象的生命周期;确保它们在容器中不被销毁,而是在不再需要时被销毁。
与Java不同,将指针设置为null不会使 释放所指向的对象;相反,如果不再有指向该对象的指针,就会发生内存泄漏。如果对象是使用new
创建的,那么必须在某个时候调用delete
。这里最好的选择是存储智能指针(shared_ptr
,或者unique_ptr
,如果可用),或者使用Boost的指针容器。
不能在容器中存储引用。您可以存储(裸)指针,但这很容易出错,因此不赞成。
因此,真正的选择是存储对象和指向对象的智能指针之间的选择。两者都有其用途。我的建议是按值存储对象,除非特殊情况另有要求。这可能发生:- 如果您需要清空对象而不将其从容器;
- ,如果需要将指向同一对象的指针存储在多个容器;
- 如果你需要处理容器的元素以多态方式。
不这样做的一个原因是为了节省空间,因为按值存储元素可能更节省空间。
添加aix的答案:
如果你想存储多态对象,你必须使用智能指针,因为容器会复制,而派生类型只复制基本部分(至少是标准的,我认为boost有一些工作方式不同的容器)。因此,您将失去对象的任何多态行为(和任何派生类状态)。
- 引用对象成员函数的成员函数
- 从 Base 引用对象调用派生类的成员
- 转换引用对象的边界框?
- 现代编译器会优化只引用对象子集的局部变量吗
- l值引用对象上的Constexpr成员函数:Clang和gcc不同意
- 将 const 类型引用对象注册为类成员对象C++
- Boost Intervocess:通过迭代通过从结构引用对象的映射进行迭代时
- 无法用2D矢量成员引用对象
- 在由引用对象传递中访问由引用对象传递的变量
- 如何使用QString引用对象名称
- 使用自动迭代器引用对象
- C++ 在另一个对象中引用对象的当前状态
- 使用共享对象和引用对象进行引用计数
- 未显式引用对象的全局对象构造函数在最终二进制文件 - LD 中被丢弃
- 引用对象的动态类型何时可以更改
- 引用对象内部的指针
- C++从全局静态函数中引用对象
- C++如何仅在没有其他人直接或间接引用对象指针时删除该指针
- 如何使用C++引用对象
- 为什么引用对象仅保存特定类型的引用