仅使用标准库创建 "Vector of References" 的标准实践
Standard Practice for Creating a "Vector of References" Using Only the Standard Libraries
我想创建一个对象,将对象放入 vector
,并且仍然能够通过仅访问 vector
来修改同一个对象。但是,我理解当一个对象是push_back()
到一个vector
时,这个对象实际上是复制到vector
。因此,访问 vector
中的对象只会访问一个相似但不同的对象。
我是C语言的初学者,所以我知道我可以创建一个指向对象的指针,并制作一个指针向量。例如vector<Object *>
。但是,好像pointers are discouraged in C++, and references are preferred. Yet, I cannot make a vector of references.
我只想使用标准库,所以 boost
对我来说是禁区。
我听说过智能指针。但是,看起来好像有多种类型的智能指针。为此目的会不会太过分了?如果智能指针确实是答案,那么我如何确定使用哪一个?
所以我的问题是:创建对象的 references/pointers 向量的标准做法是什么?
换句话说,希望下面的(伪)代码能够工作。
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;
class Object
{
public:
int field;
};
vector<Object> addToVector(Object &o)
{
vector<Object> v;
v.push_back(o);
v[0].field = 3; // I want this to set o.field to 3.
return v;
}
int main()
{
Object one;
one.field = 1;
cout << one.field << endl; // 1 as expected
Object &refone = one;
refone.field = 2;
cout << one.field << endl; // 2 as expected
vector<Object> v = addToVector(one);
cout << v[0].field << endl; // 3 as expected
cout << one.field << endl; // I want to get 3 here as well, as opposed to 2.
return 0;
}
I would like to create an object, put the object into a vector, and still be able to modify the same object by accessing only the vector. However, I understand that when an object is push_back()
to a vector, the object is actually copied into the vector. As a result, accessing the object in the vector will merely access a similar, but different object.
我几乎可以肯定这不是您想要的或 "should" 想要的。请原谅我直接打开我的答案,但除非你有很好的理由这样做,否则你可能不想这样做。
为此 - 带有引用的向量 - 要工作,您必须 保证在您持有对它们的引用时引用的对象不会被移动或破坏。如果您将它们放在矢量中,请确保矢量未调整大小。如果您像示例中那样将它们放在堆栈上,那么不要让引用向量或其副本离开该堆栈帧。如果您想将它们存储在某个容器中,请使用 std::list
(它是迭代器 - 基本上是指针 - 在插入或删除元素时不会失效)。
您已经注意到您不能拥有 "real" 个引用的向量。因此,原因是引用不可分配。考虑以下代码:
int a = 42;
int b = 21;
int & x = a; // initialisation only way to bind to something
int & y = b;
x = y;
b = 0;
之后,您从 x
获得的值将是 21,因为赋值并没有更改引用(绑定到 b
)而是引用的对象 a
.但是一个std::vector
explicitly requires这个。
您现在可以着手编写一个围绕指针的包装器,例如...
template<typename T>
struct my_ref {
T * target;
// don't let one construct a my_ref without valid object to reference to
my_ref(T & t) : target(&t) {}
// implicit conversion into an real reference
operator T &(void) {
return *target;
}
// default assignment works as expected with pointers
my_ref & operator=(my_ref const &) = default;
// a moved from reference doesn't make sense, it would be invalid
my_ref & operator=(my_ref &&) = delete;
my_ref(my_ref &&) = delete;
// ...
};
...但这毫无意义,因为 std::reference_wrapper
已经提供了:
int main (int, char**) {
int object = 21; // half of the answer
vector<reference_wrapper<int>> v;
v.push_back(object);
v[0].get() = 42; // assignment needs explicit conversion of lhs to a real reference
cout << "the answer is " << object << endl;
return 0;
}
现在有人可能会争辩为什么要像 std::reference_wrapper
这样在指针周围使用包装器,而我们也可以直接使用指针。 IMO 一个指针,能够成为 nullptr
,改变了代码的语义:当你有一个原始指针时,它 可能 是无效的。当然,你可以假设它不是,或者把它放在注释的某个地方,但最终你会依赖代码不能保证的东西(这种行为通常会导致错误)。
如果 你的向量的一个元素可以 "reference" 一个对象或者是无效的,那么原始指针仍然不是首选(对我来说):当你使用 vector 中的有效元素,然后它引用的对象实际上是从代码的多个位置引用的;它是共享的。对象的 "main" 引用应该是 std::shared_ptr
and the elements of your vector std::weak_ptr
s。然后,您可以(线程安全)在需要时获取有效的 "reference"(共享指针)并在完成后将其删除:
auto object = make_shared<int>(42);
vector<weak_ptr<int>> v;
v.push_back (object);
// ... somewhere later, potentially on a different thread
if (auto ref = v[0].lock()) {
// noone "steals" the object now while it's used here
}
// let them do what they want with the object, we're done with it ...
最后,请对我的回答持保留态度,其中大部分是基于我的意见(和经验),可能不算 "standard practice"。
我想创建一个对象,将对象放入 vector
,并且仍然能够通过仅访问 vector
来修改同一个对象。但是,我理解当一个对象是push_back()
到一个vector
时,这个对象实际上是复制到vector
。因此,访问 vector
中的对象只会访问一个相似但不同的对象。
我是C语言的初学者,所以我知道我可以创建一个指向对象的指针,并制作一个指针向量。例如vector<Object *>
。但是,好像pointers are discouraged in C++, and references are preferred. Yet, I cannot make a vector of references.
我只想使用标准库,所以 boost
对我来说是禁区。
我听说过智能指针。但是,看起来好像有多种类型的智能指针。为此目的会不会太过分了?如果智能指针确实是答案,那么我如何确定使用哪一个?
所以我的问题是:创建对象的 references/pointers 向量的标准做法是什么?
换句话说,希望下面的(伪)代码能够工作。
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;
class Object
{
public:
int field;
};
vector<Object> addToVector(Object &o)
{
vector<Object> v;
v.push_back(o);
v[0].field = 3; // I want this to set o.field to 3.
return v;
}
int main()
{
Object one;
one.field = 1;
cout << one.field << endl; // 1 as expected
Object &refone = one;
refone.field = 2;
cout << one.field << endl; // 2 as expected
vector<Object> v = addToVector(one);
cout << v[0].field << endl; // 3 as expected
cout << one.field << endl; // I want to get 3 here as well, as opposed to 2.
return 0;
}
I would like to create an object, put the object into a vector, and still be able to modify the same object by accessing only the vector. However, I understand that when an object is
push_back()
to a vector, the object is actually copied into the vector. As a result, accessing the object in the vector will merely access a similar, but different object.
我几乎可以肯定这不是您想要的或 "should" 想要的。请原谅我直接打开我的答案,但除非你有很好的理由这样做,否则你可能不想这样做。
为此 - 带有引用的向量 - 要工作,您必须 保证在您持有对它们的引用时引用的对象不会被移动或破坏。如果您将它们放在矢量中,请确保矢量未调整大小。如果您像示例中那样将它们放在堆栈上,那么不要让引用向量或其副本离开该堆栈帧。如果您想将它们存储在某个容器中,请使用 std::list
(它是迭代器 - 基本上是指针 - 在插入或删除元素时不会失效)。
您已经注意到您不能拥有 "real" 个引用的向量。因此,原因是引用不可分配。考虑以下代码:
int a = 42;
int b = 21;
int & x = a; // initialisation only way to bind to something
int & y = b;
x = y;
b = 0;
之后,您从 x
获得的值将是 21,因为赋值并没有更改引用(绑定到 b
)而是引用的对象 a
.但是一个std::vector
explicitly requires这个。
您现在可以着手编写一个围绕指针的包装器,例如...
template<typename T>
struct my_ref {
T * target;
// don't let one construct a my_ref without valid object to reference to
my_ref(T & t) : target(&t) {}
// implicit conversion into an real reference
operator T &(void) {
return *target;
}
// default assignment works as expected with pointers
my_ref & operator=(my_ref const &) = default;
// a moved from reference doesn't make sense, it would be invalid
my_ref & operator=(my_ref &&) = delete;
my_ref(my_ref &&) = delete;
// ...
};
...但这毫无意义,因为 std::reference_wrapper
已经提供了:
int main (int, char**) {
int object = 21; // half of the answer
vector<reference_wrapper<int>> v;
v.push_back(object);
v[0].get() = 42; // assignment needs explicit conversion of lhs to a real reference
cout << "the answer is " << object << endl;
return 0;
}
现在有人可能会争辩为什么要像 std::reference_wrapper
这样在指针周围使用包装器,而我们也可以直接使用指针。 IMO 一个指针,能够成为 nullptr
,改变了代码的语义:当你有一个原始指针时,它 可能 是无效的。当然,你可以假设它不是,或者把它放在注释的某个地方,但最终你会依赖代码不能保证的东西(这种行为通常会导致错误)。
如果 你的向量的一个元素可以 "reference" 一个对象或者是无效的,那么原始指针仍然不是首选(对我来说):当你使用 vector 中的有效元素,然后它引用的对象实际上是从代码的多个位置引用的;它是共享的。对象的 "main" 引用应该是 std::shared_ptr
and the elements of your vector std::weak_ptr
s。然后,您可以(线程安全)在需要时获取有效的 "reference"(共享指针)并在完成后将其删除:
auto object = make_shared<int>(42);
vector<weak_ptr<int>> v;
v.push_back (object);
// ... somewhere later, potentially on a different thread
if (auto ref = v[0].lock()) {
// noone "steals" the object now while it's used here
}
// let them do what they want with the object, we're done with it ...
最后,请对我的回答持保留态度,其中大部分是基于我的意见(和经验),可能不算 "standard practice"。