且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

CArray不会调用内存重分配的复制构造函数,现在什么?

更新时间:2023-11-13 13:53:52

复制的指针仍然指向原始数字,其中没有更长的存在,因为数组已经由于调整大小重新分配。



我猜猜CArray使用赋值,而不是复制构造。定义赋值运算符以查看是否可以修复它:

  CWeird& operator =(CWeird w){swap(w); return * this; } 
void swap(CWeird& w){int t = number; number = w.number; w.number = t; }

这通常是一个好主意,以避免复制构造和赋值之间不一致的行为。



FYI,上述代码使用惯用的方法来实现具有强大的异常安全保证的赋值语义:


  1. 返回非const引用是 operator = 的标准,因为它匹配原始类型的语义。

  2. 按值传递参数是最简单的方法来创建原始副本,并保证如果副本构造函数失败,此对象不会受到影响。

  3. 对交换的调用使用此对象切换传入的副本,从而不会抛出异常,从而以完全异常安全的方式执行赋值。

在这种情况下,只是分配数字会更简单,但我习惯性地实现我的所有分配,以避免被抓住我的裤子,如果一个未来的维护者可以复制投掷一个例外。


Suppose I have a class that requires copy constructor to be called to make a correct copy of:

struct CWeird
{
    CWeird() { number = 47; target = &number; }

    CWeird(const CWeird &other) : number(other.number), target(&number) { }

    const CWeird& operator=(const CWeird &w) { number = w.number; return *this; }

    void output()
    {
        printf("%d %d\n", *target, number);
    }

    int *target, number;
};

Now the trouble is that CArray doesn't call copy constructors on its elements when reallocating memory (only memcpy from the old memory to the new), e.g. this code

CArray<CWeird> a;
a.SetSize(1);
a[0].output();

a.SetSize(2);
a[0].output();

results in

47 47
-572662307 47

I don't get this. Why is it that std::vector can copy the same objects properly and CArray can't? What's the lesson here? Should I use only classes that don't require explicit copy constructors? Or is it a bad idea to use CArray for anything serious?

The copied pointer still points to the original number, which no longer exists, since the array has been reallocated due to the resize.

I'm guessing that CArray uses assignment rather than copy-construction. Define an assignment operator to see if this fixes it:

CWeird& operator=(CWeird w) { swap(w); return *this; }
void swap(CWeird& w) { int t = number; number = w.number; w.number = t; }

It's generally a good idea to do this anyway, to avoid inconsistent behaviour between copy-construction and assignment.

FYI, the above code uses an idiomatic approach to implementing assignment semantics with strong exception-safety guarantees:

  1. Returning a non-const reference is very much the standard for operator=, since it matches the semantics of primitive types.
  2. Passing the parameter by value is the easiest way to make a copy of the original, and guarantees that this object won't be affected if the copy constructor fails.
  3. The call to swap switches the passed-in copy with this object in a way that will never throw an exception, thus effecting the assignment in a completely exception-safe manner.

In this case, it would be simpler to just assign the number, but I habitually implement all my assignments this way to avoid being caught with my pants down if a future maintainer makes it possible for copying to throw an exception.