为什么向量通过值传递时会复制向量的内部元素?
#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具有 value sementics 的原因(或者为什么复制了内部数组)。
如果在类定义中显式定义了复制构造函数类,则当在函数调用中传递变量时,复制构造函数将确定如何复制结构/类实例。因此,我可以为我的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 +复制该数组。
答案 0 :(得分:3)
C ++允许类类型为其创建,复制,移动和销毁的含义提供自己的代码,并且隐式调用该代码,而无需进行任何明显的函数调用。这就是所谓的值语义,这是C ++在其他语言求助于create_foo(foo)
,foo.clone()
,destroy_foo(foo)
或foo.dispose()
之类的语言时所使用的语言。
每个类都可以定义以下special member functions:
这些都是您可以定义以执行所需操作的所有功能。但是它们被称为 implicitly ,这意味着此类的用户在他们的代码中看不到这些函数调用,他们希望它们做可预测的事情。您应该遵循Rule of Three/Five/Zero来确保类的行为可预测。
当然,您还已经知道其他用于共享数据的工具,例如传递引用。
标准库中的许多类都使用这些特殊的成员函数来实现特殊的行为,这些行为非常有用并有助于用户编写安全正确的代码。例如:
std::vector
在复制时将始终具有相同的元素,尽管所包含的基础数组和对象将是分开的。std::unique_ptr
包装仅拥有一个所有者的资源。要强制执行此操作,不能复制。std::shared_ptr
包装具有许多所有者的资源。目前尚不清楚何时清理此类资源,因此复制shared_ptr
会执行自动引用计数,并且仅在最后一个所有者完成后才清理资源。答案 1 :(得分:1)
这是因为vector
具有 value 语义:当您复制它时,会得到所有元素的真实副本。