0

我在 C++ 中阅读了一些关于 RVO 的内容,并发现了一个奇怪的观察结果。我运行了下面的代码..

class myClass {
  private:
    int *ptr;
    static int id;

  public:
    myClass() {
      id++;
      ptr = new int[10];
      printf("Created %p id %d this %p\n", ptr, id, this);
    }

    ~myClass() {
      delete[] ptr;
      ptr = NULL;
      printf("Deleted ptr id %d this %p\n", this->id, this);
      id--;
    }
};

int myClass::id = 0;

myClass testFunc(myClass var) {
  myClass temp;
  return temp;
}

int main() {
  myClass var1, var2;

  testFunc(var1);

  return 0;
}

我得到了o / p

Created 0x9b14008 id 1 this 0xbfe3e910
Created 0x9b14038 id 2 this 0xbfe3e914
Created 0x9b14068 id 3 this 0xbfe3e91c
Deleted ptr id 3 this 0xbfe3e91c
Deleted ptr id 2 this 0xbfe3e918
Deleted ptr id 1 this 0xbfe3e914
Deleted ptr id 0 this 0xbfe3e910

调用 testFunc 中的临时复制变量实际上会导致一些问题。它删除了 var1 的 ptr 成员,这可以从指针 0xbfe3e918 中对析构函数的调用中看出。在 valgring 下,此代码显示没有内存泄漏,但删除 [] 无效。

我有点困惑如何调用额外的析构函数,为什么没有相应的构造函数调用相同的?

4

3 回答 3

1

这实际上与返回值优化没有任何关系。仅当您使用testFunc.

您看到的问题是因为您的类只是使用默认的复制构造函数,所以当var传递给时testFuncptr成员被复制为常规指针,而不创建它指向的对象的新副本。

因此,您最终会得到两个myClass指向同一个底层int数组的对象,当调用这两个对象的析构函数时,它们会尝试删除同一个int数组两次。

于 2014-08-30T16:23:04.383 回答
0

testFunc被调用 thenvar被初始化为var1来自 main 的副本。这是使用默认的复制构造函数完成的,因此“缺少”构造函数调用。只是调用了另一个构造函数。

所以问题是,现在那varvar1那意味着它var.ptr必须具有与 相同的值的副本var1.ptr

1.解决方案是提供自己的复制构造函数来处理这种情况。一种解决方案是进行深层复制:

myClass(const myClass& o) {
    id++;
    ptr = new int[10];
    memcpy(o.ptr, ptr, 10);
    printf("Created copy %p of %p id %d this %p\n", ptr, o.ptr, id, this);
}

2.另一种解决方案是使用shared_ptr可以跟踪具有特定指针副本的对象数量:

//int *ptr;           //Replace this
shared_ptr<int> ptr;  //With this

//Initialize ptr:
myClass():ptr(new int[10]) {
    //...
}

// Also eliminate cleenup. It's handled by shared_ptr:
~myClass() {
    printf("Deleted ptr id %d this %p\n", this->id, this);
    id--;
}

shared_ptr方法的问题在于它们仍然共享相同的指针。这意味着它不像通常预期的那样表现得像副本。如果指向的值ptr永远不会改变,那可能没问题。

我更喜欢解决方案1

于 2014-08-30T16:37:10.670 回答
0

var1按值作为函数参数传递会创建一个副本。这是通过(隐式定义的)复制构造函数完成的,这就是为什么您的程序不打印任何内容的原因——您只在默认构造函数中打印一些内容。

您现在遇到了一个大问题:对象的两个副本都包含指向同一个数组的指针,并且两者都会在销毁时尝试将其删除。这种双重删除是一个错误,会导致未定义的行为。

要修复它,请遵循三规则以使该类正确可复制(或不可复制);或者停止使用原始内存进行所有这些危险的处理,并使用std::vector或类似的方式为您正确管理阵列。

于 2014-08-30T16:23:19.400 回答