堆栈上分配的矢量元素


#include <iostream>
#include <vector>
using namespace std;
struct A {
int i = 0;
};
void append(vector<A>& v) {
auto a = v.back(); // is a allocated on the stack? Will it be cleaned after append() returns?
++a.i;
v.push_back(a);
}
void run() {
vector<A> v{};
v.push_back(A{}); // is A{} created on the stack? Will it be cleaned after run() returns?
append(v);
for (auto& a : v) {
cout << a.i << endl;
}
}
int main() {
run();
return 0;
}

上面的代码按预期打印:

0
1

但我有两个问题:

  1. 是否在堆栈上创建了A{}?run((返回后,它会被清理吗
  2. 堆栈上分配了a吗?append((返回后会被清除吗

更新:

#include <iostream>
#include <vector>
using namespace std;
struct A {
int i = 0;
A() { cout << "+++Constructor invoked." << endl; }
A(const A& a) { cout << "Copy constructor invoked." << endl; }
A& operator=(const A& a) {
cout << "Copy assignment operator invoked." << endl;
return *this;
};
A(A&& a) { cout << "Move constructor invoked." << endl; }
A& operator=(A&& a) {
cout << "Move assignment operator invoked." << endl;
return *this;
}
~A() { cout << "---Destructor invoked." << endl; }
};
void append(vector<A>& v) {
cout << "before v.back()" << endl;
auto a = v.back();
++a.i;
cout << "before v.push_back()" << endl;
v.push_back(a);
cout << "after v.push_back()" << endl;
}
void run() {
vector<A> v{};
v.push_back(A{});
cout << "entering append" << endl;
append(v);
cout << "exited append" << endl;
for (auto& a : v) {
cout << a.i << endl;
}
}
int main() {
run();
return 0;
}

输出:

+++Constructor invoked.
Move constructor invoked.
---Destructor invoked.
entering append
before v.back()
Copy constructor invoked.
before v.push_back()
Copy constructor invoked.
Copy constructor invoked.
---Destructor invoked.
after v.push_back()
---Destructor invoked.
exited append
0
0 // I understand why it outputs 0 here. I omitted the actual work in my copy/move constructors overloads.
---Destructor invoked.
---Destructor invoked.

我更新了问题中的代码,添加了复制/移动构造函数。我发现复制构造函数在追加中被调用了3次。我理解autoa=v.back((;需要一份,但另外两份可能应该避免?

C++规范实际上并没有说明。

使用v.push_back(A{})A{}部分创建一个临时对象,然后将其移动或复制到向量中,然后丢弃该临时对象。

与局部变量相同,实际上;堆叠";实际上C++标准从来没有提到过,它只是告诉生命周期应该如何处理。编译器可能使用";堆叠";是一个实现细节。

话虽如此,大多数C++编译器将使用";堆叠";以存储局部变量。例如类似于append函数中的变量a。至于为v.push_back(A{})创建的临时对象,您需要检查生成的程序集代码。

对于生命周期,一旦push_back函数返回,临时对象A{}的生命周期就结束。当append函数返回时,append函数中a的寿命结束。

在此函数中

void append(vector<A>& v) {
auto a = v.back(); // is a allocated on the stack? Will it be cleaned after append() returns?
++a.i;
v.push_back(a);
}

变量CCD_ 11具有自动存储持续时间并且是函数的局部变量。退出该功能后,它将不活动。

在该功能中,

void run() {
vector<A> v{};
v.push_back(A{}); // is A{} created on the stack? Will it be cleaned after run() returns?
append(v);
for (auto& a : v) {
cout << a.i << endl;
}
}

变量CCD_ 12再次具有自动存储持续时间并且是函数的局部变量。当函数完成执行时,变量将被销毁。向量的所有元素(放置在堆中(也将由于向量的析构函数而被销毁。

考虑以下演示程序。

#include <iostream>
#include <vector>
struct A {
int i = 0;
};
int main() 
{
std::vector<A> v;

std::cout << "&v = " << &v << "nn";

A a;

std::cout << "&a = " << &a << "nn";

v.push_back( a );

std::cout << "&v = " << &v << 'n';
std::cout << "&a = " << &a << 'n';
std::cout << "&v[0] = " << &v[0] << "nn";
++a.i;

v.push_back( a );

std::cout << "&v = " << &v << 'n';
std::cout << "&a = " << &a << 'n';
std::cout << "&v[0] = " << &v[0] << 'n';
std::cout << "&v[1] = " << &v[1] << "nn";
return 0;
}

它的输出可能看起来像

&v = 0x7ffc27288dd0
&a = 0x7ffc27288dcc
&v = 0x7ffc27288dd0
&a = 0x7ffc27288dcc
&v[0] = 0x55725232ee80
&v = 0x7ffc27288dd0
&a = 0x7ffc27288dcc
&v[0] = 0x55725232eea0
&v[1] = 0x55725232eea4

正如你所看到的,向量v和对象a的地址看起来相似,因为它们被分配在函数的相同外部块范围内,并且具有自动存储持续时间。

&v = 0x7ffc27288dd0
&a = 0x7ffc27288dcc

当在向量上推送新值时,它们不会改变。

然而,矢量元素的地址,例如

&v[0] = 0x55725232ee80
&v[0] = 0x55725232eea0
&v[1] = 0x55725232eea4

具有不同的表示,并且可以在向向量添加新元素时进行更改,因为它们的内存可以动态地重新分配。

编辑:更新问题后,请考虑当向向量中添加新元素时,可以通过调用复制构造函数重新分配向量中的元素。您可以使用方法reserve来保留足够的内存以避免其重新分配,也可以使用方法emplace_back

是在堆栈上分配的吗?

没有";堆叠";语言存储。a具有自动存储功能。

就语言实现而言,这通常意味着变量可能存储在寄存器中、堆栈中,或者不存储在任何位置。

append((返回后会被清除吗?

是。当自动变量超出范围时,它们会自动销毁。

是否在堆栈上创建了A{}?

A{}是一个临时对象。该语言对临时对象的存储类有点模糊,但对生存期很清楚。

run((返回后会被清除吗?

在这种情况下,临时对象将在完整表达式的末尾销毁,即run返回之前。


堆栈上分配的矢量元素?

否。矢量元素是在动态存储中创建的。


更新

但另外两份可能应该避免?

如果你的最终目标是获得一个有两个元素的向量,你可以避免所有这样的副本:

std::vector<A> v(2);

相关内容

  • 没有找到相关文章

最新更新