在对 valgrind 进行大量调查之后,我得出结论,std::vector 会复制您想要 push_back 的对象。
这是真的吗?向量不能在没有副本的情况下保留对象的引用或指针?!
谢谢
*
或 &
修改类型以创建指针或引用,否则两个变量引用同一对象是不可能的。
push_back
的签名中推断出一些东西:它需要一个 const&
。要么将值丢弃(无用),要么有一种检索方法。因此,我们查看 back
的签名,它返回纯 &
,因此原始值被复制或 const
已被默默丢弃(非常糟糕:潜在的未定义行为)。因此,假设 vector
的设计者是理性的(vector<bool>
无法承受),我们得出结论,它会复制。
是的,std::vector<T>::push_back()
创建参数的副本并将其存储在向量中。如果要在向量中存储指向对象的指针,请创建 std::vector<whatever*>
而不是 std::vector<whatever>
。
但是,您需要确保指针引用的对象在向量持有对它们的引用时保持有效(使用 RAII 习语的智能指针解决了这个问题)。
从 C++11 开始,所有标准容器(std::vector
、std::map
等)都支持移动语义,这意味着您现在可以将右值传递给标准容器并避免复制:
// Example object class.
class object
{
private:
int m_val1;
std::string m_val2;
public:
// Constructor for object class.
object(int val1, std::string &&val2) :
m_val1(val1),
m_val2(std::move(val2))
{
}
};
std::vector<object> myList;
// #1 Copy into the vector.
object foo1(1, "foo");
myList.push_back(foo1);
// #2 Move into the vector (no copy).
object foo2(1024, "bar");
myList.push_back(std::move(foo2));
// #3 Move temporary into vector (no copy).
myList.push_back(object(453, "baz"));
// #4 Create instance of object directly inside the vector (no copy, no move).
myList.emplace_back(453, "qux");
或者,您可以使用各种智能指针来获得几乎相同的效果:
std::unique_ptr
示例
std::vector<std::unique_ptr<object>> myPtrList;
// #5a unique_ptr can only ever be moved.
auto pFoo = std::make_unique<object>(1, "foo");
myPtrList.push_back(std::move(pFoo));
// #5b unique_ptr can only ever be moved.
myPtrList.push_back(std::make_unique<object>(1, "foo"));
std::shared_ptr
示例
std::vector<std::shared_ptr<object>> objectPtrList2;
// #6 shared_ptr can be used to retain a copy of the pointer and update both the vector
// value and the local copy simultaneously.
auto pFooShared = std::make_shared<object>(1, "foo");
objectPtrList2.push_back(pFooShared);
// Pointer to object stored in the vector, but pFooShared is still valid.
std::make_unique
(令人讨厌)仅在 C++14 及更高版本中可用。如果要编译这些示例,请确保告诉编译器相应地设置其标准一致性。
auto pFoo =
来避免重复;并且可以删除所有 std::string
强制转换(从字符串文字到 std::string
的隐式转换)
make_unique
可以很容易地在 C++11 中实现,所以对于坚持使用 C++11 编译器的人来说,这只是一个小小的烦恼
template<typename T, typename... Args> unique_ptr<T> make_unique(Args&&... args) { return unique_ptr<T>{new T{args...}}; }
std::move()
与 std::shared_ptr
一起使用,则原始共享指针可能会更改其指针,因为所有权已传递给向量。见这里:coliru.stacked-crooked.com/a/99d4f04f05e5c7f3
是的,std::vector
存储副本。 vector
应该如何知道您的对象的预期生命周期是多少?
如果您想转移或共享对象的所有权,请使用指针,可能是像 shared_ptr
(在 Boost 或 TR1 中找到)这样的智能指针来简化资源管理。
boost::ptr_vector
吗?
class Foo { typedef boost::shared_ptr<Foo> ptr; };
来写 Foo::ptr
。
shared_ptr
并不完全是一劳永逸。请参阅 stackoverflow.com/questions/327573 和 stackoverflow.com/questions/701456
std::vector 总是复制存储在向量中的任何内容。
如果您保留一个指针向量,那么它将制作指针的副本,而不是指针指向的实例。如果您正在处理大型对象,则可以(并且可能应该)始终使用指针向量。通常,使用适当类型的智能指针向量有利于安全目的,因为否则处理对象生命周期和内存管理可能会很棘手。
std::vector 不仅会复制您要推回的任何内容,而且集合的定义表明它将这样做,并且您不能在向量中使用没有正确复制语义的对象。因此,例如,您不要在向量中使用 auto_ptr。
如果您不想要副本;那么最好的方法是使用指针向量(或其他用于相同目标的结构)。如果你想要副本;直接使用 push_back()。你别无选择。
与 C++11 相关的是 emplace
系列成员函数,它允许您通过将对象移动到容器中来转移对象的所有权。
用法的习惯用法看起来像
std::vector<Object> objs;
Object l_value_obj { /* initialize */ };
// use object here...
objs.emplace_back(std::move(l_value_obj));
左值对象的移动很重要,否则它将作为引用或常量引用转发,并且不会调用移动构造函数。
emplace_back
不应与现有对象一起使用,而应在适当位置构造一个新对象
为什么需要大量 valgrind 调查才能发现这一点!只需用一些简单的代码证明给自己看,例如
std::vector<std::string> vec;
{
std::string obj("hello world");
vec.push_pack(obj);
}
std::cout << vec[0] << std::endl;
如果打印“hello world”,则该对象必须已被复制
push_back
将执行移动而不是复制。 (可以使用std::move()
将对象转换为右值引用。)emplace_back
来避免任何复制或移动(在容器提供的位置构造对象)。