【问题标题】:C++ Shallow and deep copying - reflecting changes in the num_items of a vectorC++ 浅复制和深复制 - 反映向量的 num_items 的变化
【发布时间】:2014-06-17 13:27:57
【问题描述】:

我目前正在大学学习 C++ 课程。我理解使用向量进行浅拷贝和深拷贝的一般概念,但是我的教科书中有一个例子让我感到困惑。

请假设它是一个实现不佳的向量,没有定义复制构造函数,因此它只执行数据的浅拷贝。

我明白第一部分发生了什么

在声明中

vector<int> v2(v1);

vector v1 作为常量引用参数传递给向量副本 构造函数,所以v1不能被改变,那么变量v2就是 初始化为向量v1 的副本。每个数据字段将是 复制,以后对v2 所做的任何更改都不应影响v1。什么时候 v1.the_data 中的值被复制,v1.the_datav2.the_data 将指向同一个数组

因为v1.the_datav2.the_data 指向同一个对象,所以 声明

v1[2] = 10;

也改变了v2[2]。因此,v2 被认为是浅拷贝 v1.

但是我很难理解这部分。我不太清楚为什么v2.num_items 在浅拷贝中也不会改变。

声明

v1.push_back(20);

20 插入v1[5] 并将v1.num_items 更改为6,但将 不改v2.num_items

我目前对此的想法是 v1.the_datav2.the_data 指向内存中的同一个位置,因此它们“共享”同一个向量,因此当将 20 添加到它的末尾时,两个向量都应该获得一个额外的整数。

如果能帮助我理解为什么在修改 v1 时不会更改 v2 的项目数,我将不胜感激。

【问题讨论】:

  • 在您教科书的“引用部分”中?!
  • 除非引用的部分描述了错误实现向量的行为,否则它就是垃圾。
  • 它来自我的导师提供的讲义。我认为它是一个错误实现的向量。我将编辑我的帖子以明确这一点。

标签: c++ vector copy deep-copy shallow-copy


【解决方案1】:

假设我们谈论的是标准std::vector

当你在这个语句中复制向量时:

vector<int> v2(v1);

v2 是通过复制 v1 的每个元素来构建的。 v1 和 v2 不共享任何内存。

这部分:

v1.the_data 和 v2.the_data 都将指向同一个数组

因为 v1.the_data 和 v2.the_data 指向同一个对象,

错了。

您可以通过将每个向量的底层数组地址与data()member function 进行比较来说服自己。

编辑:

假设你足够疯狂以至于不使用 std::vector 并使用在复制时会“共享”其后端数组的实现(我不会谈论这种设计的问题:谁拥有数组?谁 delete[] 它?)

你老师提出的问题是,当v1被修改(例如添加一个元素)时,v2不知道它,并且大小不变。

对一个向量所做的任何push_back(或类似的)都应该被数组的所有其他所有者观察,以正确反映数组的大小。

要么:

1) 你实现了某种观察者模式,让每个向量都知道任何修改(这比听起来要困难)

2) 您使用技巧将长度存储在后端数组本身中。

当通过向量引用之一修改“共享”数组时,您会遇到类似的问题,使每个迭代器都无效......一场噩梦! STL 容器全部被设计为管理自己的内存是有充分理由的,因此总是提供深拷贝语义。

【讨论】:

  • 编辑了我的问题,假设它不是 std::vector。如果它是 std::vector 的实现,我假设它会执行深层复制。
【解决方案2】:

您的教科书是否在谈论标准库中的std::vector?如果是这样,那就错了。 vector&lt;int&gt; v2(v1); 复制结构 v2 来自v1。这是一个深拷贝,两个容器不共享存储,完全分开。

相反,如果这是一个糟糕实现的vector 类并且容器共享存储,那么更改一个现有 元素将反映在另一个中。像push_back 这样的操作改变了一个容器的num_items 但没有改变另一个容器的大小,这会导致它们在大小上存在分歧。

【讨论】:

  • 对不起,我应该添加一个假设,即它没有使用 std::vector 而是一个糟糕的实现。
  • @juleszero:更新以反映矢量类设计不佳的问题之一。
【解决方案3】:

该语句似乎假设了一个特定的实现 vector(不符合std::vector)。认为, 例如,我们有一个非常幼稚的实现:

template <typename T>
class Vector
{
    T* myData;
    int mySize;
    int myCapacity;
public:
    void push_back( T const& newValue )
    {
        if ( mySize == myCapacity ) {
            //  Extend the capacity...
        }
        myData[mySize] = newValue;
        ++ mySize;
    }
    T& operator[]( int index )
    {
        return myData[index];
    }
};

如果你没有复制构造函数,当你复制向量时, 所有三个变量的结果都是一样的:两个向量都有 指向相同数据、相同大小和相同容量的指针。 但这些是副本:当你使用[]时,你修改了内存 由myData 指向,这在两个向量中是相同的;什么时候 你在v1 上执行push_back,你更新v1size, 在其大小的本地副本中。

当然,这个实现在很多方面都是幼稚的。 像std::vector 这样的良好实现需要 相当多的思考,不仅仅是因为如果需要深拷贝 语义,但也出于异常安全的原因( T 的构造函数可能会抛出),并避免强加 不必要的要求(特别是默认构造函数)。

另外,如果我尝试使用实现不佳的向量作为浅拷贝的示例,我不会将其称为 vector,因为这会立即让人联想到 std::vector 的形象,这应该不差已实现(并且不在我所知道的库实现中)。

【讨论】:

    【解决方案4】:

    理解这个问题中的陈述的问题是我们是否必须将向量视为 std::vector 或作为理论实现。 std::vector 不允许浅拷贝,原因在语句中给出:因此不能尊重不变量。

    现在采用“the_data”和“num_items”成员的理论实现。这里复制向量应该给出一个深拷贝,但仅仅复制“the_data”会给出一个浅拷贝,因为只复制了一个指针。这带来了几个问题:调整一个向量中的实际数据将导致另一个向量中的状态不一致,并且无法再进行内存管理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-11-18
      • 1970-01-01
      • 2014-11-30
      • 1970-01-01
      • 2020-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多