【问题标题】:c++ swap global and local vector?c++交换全局和局部向量?
【发布时间】:2021-02-19 01:35:29
【问题描述】:

我想交换一个全局向量和一个局部向量,我的问题是局部向量在他的堆栈上。交换运算符会通过指针交换两个向量,所以交换后全局向量将指向局部向量,在堆栈上。

离开作用域时,本地向量会被释放,我知道向量的内容被交换了,但是y在栈上,为什么离开后x仍然有效func 范围,y 将在此之后发布。这是怎么做到的?

#include <iostream>
#include <vector>

using namespace std;

vector<int> x = {2, 3, 4};

void func() {
    vector<int> y = {1, 2, 3};
    cout << "y before: " << &y << endl;
    x.swap(y);
    cout << "y after: " << &y << endl;
}

int main()
{
    cout << "x before: " << &x << endl;
    func();
    cout << "x after: " << &x << endl;
    return 0;
}

【问题讨论】:

  • “为什么下面的代码仍然有效”到底是什么意思? “仍在工作”是什么意思?你预计会发生什么?
  • 我知道向量的内容被交换了,但是y在堆栈上,为什么x在离开func作用域后仍然有效,之后y会重新释放。这是怎么做到的?
  • 为什么不是“离开功能后有效”?它仍然是全局范围内的对象。交换其内容不会改变这一点。只有在本地范围内的对象,即y,会在函数范围结束后被销毁。
  • @dahohu527 x 没有指向任何地方,因为x 不是指针。
  • x 包含y 以前的内容。 x 的位置不会改变。如果您将丰田的发动机换成本田的发动机,然后本田(带有丰田的发动机)被开走,您旁边还有一辆丰田,里面装有本田的发动机。本田发动机没有发生任何事情。换了之后,它还在丰田车里。

标签: c++ swap


【解决方案1】:

“交换操作符将通过指针交换两个向量” -- swap 的实现细节并不是你真正关心的。但是您似乎对正在交换哪个指针(如果有的话)感到困惑。

我可以肯定地说 pointer &amp;y 不是 正在交换的东西。您的func() 调用将在交换前后显示完全相同的指针值,因为y 是堆栈上的一个对象。同样,&amp;x 指向全局范围级别的对象。

vector 内部,存储值的实际内存可能会被交换,因为两个向量共享相同的(默认)分配器,因此允许简单地切换指针以提高效率。但实际持有它们的矢量对象绝对不会在内存中切换位置。

如果要检查内存是否被交换,则可以输出data()成员函数返回的指针:

cout << "x before: " << x.data() << endl;
x.swap(y);
cout << "x after: " << x.data() << endl;

【讨论】:

  • 感谢您的回答!我知道向量的内容被交换了,但是y在堆栈上,为什么离开func范围后x仍然有效,y之后会重新释放。
  • 你是说经过这个解释你不明白其中的区别吗?然后我认为您需要尝试编写自己的简单矢量类,您可能会得到它。 x 是有效的,因为它的生命周期处于全局范围级别。 what y 包含无关紧要——yfunc() 退出时被销毁。假设在向量内部发生了指针交换——这意味着两个对象交换了它们正在管理的内存。该内存是在堆上分配的,不受堆栈规则的约束。每个对象仍然各自管理一个内存块。
  • 我知道x 本身在堆之上,而y 在堆栈上。我可以将交换简化如下x = std::move(y),std::move 将修改指向的数据。如果y 的数据在堆栈上,而std::move 会将x 的数据指向y 的数据,那么问题是std::move 有什么特别的作用吗?
【解决方案2】:

被调用的交换函数是std::vector::swap。此函数交换向量的内容。

您对 cout 的调用会打印向量的地址。每次调用时,被打印地址的变量仍然有效。

向量 y 分配在栈上,但两个向量的元素都分配在堆上。交换之后,向量 x 指向最初分配给向量 y 的元素。当向量 y 从堆栈中移除时,最初分配给向量 x 的元素也被删除。

【讨论】:

    【解决方案3】:

    我看到一些困惑,所以我建议你改变你的心态:

    • 从栈/堆/全局的角度思考到storage duration的角度思考
    • 从指针方面的思考到对象、资源和所有权方面的思考

    存储时间

    程序中的所有对象都具有以下存储期限之一:

    • 自动存储时间。对象的存储在封闭代码块的开头分配并释放 在末尾。所有本地对象都有这个存储持续时间,除了那些 声明为 static、extern 或 thread_local。
    • 静态存储持续时间。对象的存储空间在程序开始时分配,在程序启动时释放 结束。该对象仅存在一个实例。声明的所有对象 命名空间范围(包括全局命名空间)有这个存储 持续时间,加上那些用静态或外部声明的。
    • 线程 存储持续时间。 [...](C++11 起)
    • 动态存储持续时间。使用动态内存为每个请求分配和释放对象的存储空间 分配函数。

    x 具有静态存储持续时间。这意味着x 在整个程序期间都是有效的。

    y 具有自动存储期限。这意味着对象在封闭代码块的持续时间内是“活动的”。对象在代码块的开头开始它的生命,在代码块的结尾结束它的生命。在这种情况下,代码块是函数的主体。 y 对象将在函数调用期间处于活动状态。

    无论对象的类型或您对xy 执行的操作如何,这都是正确的。如果xy 是整数、向量、字符串、指针、枚举或实际上任何东西都不会以任何方式、形状或形式改变这一点。无论您对它们进行什么操作(交换、清除、分配、复制、移动……),以上内容都保持不变:x 在整个程序期间都可以访问,y 可以访问而它表示的对象是活着的(如上所述)。

    对象、资源、所有权

    xystd::vector 对象。它们具有如上所述的存储期限。

    每个std::vector 可能有一个或多个他们负责的对象。例如,具有 3 个元素的 std::vector&lt;T&gt; 具有并负责:1 个存储其元素的内存对象和 3 个 T 对象。这些对象具有动态存储持续时间。这些对象的存储由std::vector 对象请求分配和释放。 std::vector 是这些对象的所有者。它负责创建/销毁这些对象。

    你的例子

    x.swap(y):如上所述,任何操作都不会改变xy的存储时长。在此操作之后,您将拥有与以前相同的对象 x(除了它具有不同的内容)和与以前相同的对象 y(除了它具有不同的内容)。此操作所做的是在其资源的两个向量之间转移所有权。在此操作期间,x 拥有的内存和元素对象的所有权更改为 yy 拥有的内存和元素对象的所有权更改为x

    所以退出函数后func:

    您绝对仍然可以访问x,因为对象x 在程序的整个运行时都处于活动状态(它具有静态存储持续时间)。函数调用无法改变这一点。

    您的程序控制流程

    让我们打破程序的控制流程:

    • vector&lt;int&gt; x = {2, 3, 4};:作为静态初始化的一部分,创建静态存储对象x。作为其初始化的一部分,它创建并承担一些所需资源的所有权(1 个内存资源和 3 个元素,从现在开始我统称为{2, 3, 4})(这些资源是具有动态存储持续时间的对象:它们是根据请求创建的,并将应其所有者的要求销毁)

    • func()电话

      • vector&lt;int&gt; y = {1, 2, 3};:对象y创建时自动存储时长;作为其初始化的一部分,它创建并承担一些所需资源的所有权(内存和元素,从现在开始我统称为{1, 2, 3})(这些资源是具有动态存储持续时间的对象:它们是根据请求创建的,将被销毁应其所有者的要求)
      • x.swap(y) 致电:
        • 以前由x ({2, 3, 4}) 拥有的资源将所有权更改为y
        • 以前由y ({1, 2, 3}) 拥有的资源将所有权更改为x
      • }(函数结束):自动存储持续时间对象y 退出其范围。作为其销毁的一部分,它要求销毁其拥有的资源;此时它拥有{2, 3, 4},所以这些被销毁了。 y 被销毁。从此时起,访问y{2, 3, 4} 是非法的,因为它们的生命周期已经结束。
    • 在main中,在func()调用之后:x是有效的(它具有静态存储持续时间)并且它拥有{1, 2, 3}(它从y“窃取”的资源)。

    • main之后,程序结束之前:静态存储对象x被销毁。作为其销毁的一部分,它要求销毁其拥有的资源:@​​987654377@

    【讨论】:

      【解决方案4】:

      “交换操作符会通过指针交换两个向量,所以交换后全局向量会指向栈上的局部向量。”

      不,交换运算符(很可能)将“通过指针”交换两个向量的数据,因此交换后,全局向量将(内部)指向局部向量的 >前数据,位于上。

      上述陈述并非完全准确,但确实突出了问题陈述的主要缺点。相比之下,其他不准确之处(例如假设“堆栈”和“堆”是有用的描述)是次要的。在某个地方,交换指针到两个向量的数据的想法变成了交换指针到两个向量的想法,结果被误解了。


      通常,向量由指向向量元素的指针和一些簿记数据组成。交换交换指针(和簿记),这导致数据交换为固定成本,无论向量有多大。

      这是一个可能的向量实现的一部分,重点是被交换的指针。

      // Abridged outline of a possible vector implementation
      template <class T, class Allocator>
      class vector {
          T* data;   // Could (should) be a smart pointer instead of a raw one.
          // Other data members
      
        public:
          void swap(vector & other)
          {
              swap(data, other.data);   // Swap pointers.
              // Swap other data members.
          }
      
          // Other functions
      };
      

      在此实现中交换向量内部的指针,而不是指向向量的指针。 (其他实现可能有不同的实现细节。)

      【讨论】:

        猜你喜欢
        • 2015-10-04
        • 1970-01-01
        • 1970-01-01
        • 2022-08-22
        • 2014-12-11
        • 1970-01-01
        • 1970-01-01
        • 2023-03-14
        • 1970-01-01
        相关资源
        最近更新 更多