当向量按值传递时,为什么复制向量的内部元素?
#include<vector>
using namespace std;
// this func won't modify v[2].
// Meaning v[2] (and the whole inner array) was copied
// when v is passed to the func?
void modify(vector<int> v) {
v[2] = 100;
}
// this func modify v[2]
void modify(vector<int>& v) {
v[2] = 100;
}
int main() {
vector<int> v = {1,2,3,4,5,6};
// still same
modify(v);
// modified
modified2(v);
}
我发现奇怪的是,当按值传递向量时,会复制向量的实际内容。我认为std :: vector实现必须具有一个指针字段,该字段映射到实际数组所在的堆上的地址。因此,即使通过值传递向量,地址也应保持不变,指向相同的内容。像这样:
#include<iostream>
using namespace std;
// a dummy wrapper of an array
// trying to mock vector<int>
class vector_int {
public:
int* inner_array; // the actual array
vector_int(int *a) {
inner_array = a;
}
int* at(int pos) {
return inner_array+pos;
}
};
// this passes the "mocked vector" by value
// but 'inner_array' is not copied
void modify(vector_int v) {
*(v.at(2)) = 10;
}
int main() {
int* a = new int[3] {1,2,3};
vector_int v = vector_int(a);
modify(v); // v[2] is modified
}
这个关于std :: vector实现的假设正确吗?通过值传递复制矢量内容的原因是什么?
多亏了igel的回答和UnholySheep的评论,我才弄清了std :: vector具有价值的重要性(或内部数组被复制的原因)的原因。
如果在类定义中显式定义了复制构造函数类,则复制构造函数将确定在函数调用中传递变量时如何复制struct / class实例。因此,我可以为我定义一个复制构造函数vector_int
,在其中复制整个内容inner_array
,例如
#include<iostream>
using namespace std;
class vector_int {
public:
int* inner_array;
int len;
vector_int(int *a, int len) {
inner_array = a;
this->len = len;
}
int* at(int pos) {
return inner_array+pos;
}
// this is the copy constructor
vector_int(const vector_int &v2) {
inner_array = new int;
for (int i =0; i < v2.len; i++) {
*(inner_array+i) = *(v2.inner_array+i);
}
}
};
// Yay, the vector_int's inner_array is copied
// when this function is called
// and no modification of the original vector is done
void modify(vector_int v) {
*(v.at(2)) = 10;
}
int main() {
int* a = new int[3] {1,2,3};
vector_int v = vector_int(a,3);
//
modify(v);
}
我在本地计算机(g ++ Apple LLVM版本10.0.0)上检查了stdlib实现的源代码。std :: vector定义了一个复制构造函数,看起来像这样
template <class _Tp, class _Allocator>
vector<_Tp, _Allocator>::vector(const vector& __x)
: __base(__alloc_traits::select_on_container_copy_construction(__x.__alloc()))
{
size_type __n = __x.size();
if (__n > 0)
{
allocate(__n);
__construct_at_end(__x.__begin_, __x.__end_, __n);
}
}
它看起来像为实际复制的数组做malloc +复制该数组。
C ++允许类类型为其创建,复制,移动和销毁的含义提供自己的代码,并且隐式调用该代码,而无需进行任何明显的函数调用。这就是所谓的语义值,它就是C ++使用,而其他语言诉诸一些事情,如create_foo(foo)
,foo.clone()
,destroy_foo(foo)
或foo.dispose()
。
每个类都可以定义以下特殊成员函数:
您可以定义所有这些功能以执行所需的任何操作。但是它们被隐式调用,这意味着此类的用户在他们的代码中看不到这些函数调用,他们希望它们做可预测的事情。您应遵循“三/五/零”规则,确保您的班级表现可预测。
当然,您已经知道其他用于共享数据的工具,例如传递引用。
标准库中的许多类都使用这些特殊的成员函数来实现特殊的行为,这些行为非常有用并有助于用户编写安全正确的代码。例如:
std::vector
复制时,尽管底层数组和包含的对象将是单独的,但它们将始终具有相同的元素。std::unique_ptr
包装仅拥有一个所有者的资源。要强制执行此操作,无法将其复制。std::shared_ptr
包装具有许多所有者的资源。何时清除此类资源尚不完全清楚,因此复制shared_ptr
会执行自动引用计数,并且仅在最后一个所有者完成后才清除该资源。本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句