【问题标题】:C++ object destroyed more than onceC++ 对象不止一次被破坏
【发布时间】:2014-05-26 03:35:48
【问题描述】:

在编写一些将数据结构(部分)加载到图形内存中的代码时,我对这种意外行为感到惊讶。

我已将代码简化为以下内容:

#include <iostream>
#include <vector>
using namespace std;

struct Data{
    int data;
    Data(int x){
        data = x;
        cout<<"+ Data"<<endl;
    }
    ~Data(){cout<<"- Data"<<endl;}
};

struct Handle{
    // imagine some handles to video memory here
    Handle(Data d){
        // (upload data to video memory)
        cout<<"+ Handle"<<endl;
    }
    ~Handle(){
        // (deallocate the data from video memory)
        cout<<"- Handle"<<endl;
    }
};

int main(){
    vector<Handle> container;
    cout<<"start"<<endl;
    for(int i=0; i<4; i++){
        container.emplace_back(Data(i));
        cout<<endl;
    }
    cout<<"end"<<endl;
}

我的预期:

start
+ Data
+ Handle
- Data

+ Data
+ Handle
- Data

+ Data
+ Handle
- Data

+ Data
+ Handle
- Data

end
-Handle
-Handle
-Handle
-Handle

我得到了什么:

start
+ Data
+ Handle
- Data
- Data

+ Data
+ Handle
- Data
- Handle
- Data

+ Data
+ Handle
- Data
- Handle
- Handle
- Data

+ Data
+ Handle
- Data
- Data

end
- Handle
- Handle
- Handle
- Handle

谁能解释这种行为?

【问题讨论】:

  • 你应该声明一个复制构造函数,这样你就可以真正追踪创建了多少对象......
  • Handles 构造函数中有一个未使用的参数。你指望过吗?另外,container.emplace_back(i);,这样您就不会做不必要的临时工作。
  • emplace_back 是 C++11.. 现在不需要它,因为在 C++11 中没有使用 push_back 临时创建:void push_back(T&amp;&amp; value);
  • @Macmade 谢谢!这解释了额外的析构函数调用。
  • cppguy 回答答案中的附加数据副本。我添加这条评论是为了提到额外的句柄可以通过在向量上使用reserve() 来消除,所以它不需要调整大小。

标签: c++ constructor destructor instantiation raii


【解决方案1】:

对代码的这种修改可能会帮助您更好地了解正在发生的事情。

#include <iostream>
#include <vector>
using namespace std;

struct Data{
    int data;
    static int allcount;
    int count;
    Data(int x) : count(allcount) {
        data = x;
        allcount++;
        cout<<"+ Data "<< count <<endl;
    }
    Data(const Data &d) : count(allcount) {
        allcount++;
        cout<<"+ Data(copy of " << d.count << ") "<< count <<endl;
    }
    ~Data(){cout<<"- Data "<< count <<endl;}
};

struct Handle{
    static int allcount;
    int count;
    // imagine some handles to video memory here
    Handle(Data d) : count(allcount) {
        allcount++;
        // (upload data to video memory)
        cout<<"+ Handle " << count << endl;
    }
    Handle(const Handle &h) : count(allcount) {
        allcount++;
        cout<<"+ Handle(copy of " << h.count << ") "<< count <<endl;
    }
    ~Handle(){
        // (deallocate the data from video memory)
        cout<<"- Handle " << count <<endl;
    }
};

int Handle::allcount=0;
int Data::allcount=0;

int main(){
    vector<Handle> container;
    cout<<"start"<<endl;
    for(int i=0; i<4; i++){
        container.emplace_back(Data(i));
        cout<<endl;
    }
    cout<<"end"<<endl;
}

它的作用是使用一个整数来跟踪每个分配和解除分配,以唯一标识每个。复制构造函数也被显式添加。当我在我的系统上运行它时,我得到以下信息:

start
+ Data 0
+ Data(copy of 0) 1
+ Handle 0
- Data 1
- Data 0

+ Data 2
+ Data(copy of 2) 3
+ Handle 1
- Data 3
+ Handle(copy of 0) 2
- Handle 0
- Data 2

+ Data 4
+ Data(copy of 4) 5
+ Handle 3
- Data 5
+ Handle(copy of 2) 4
+ Handle(copy of 1) 5
- Handle 2
- Handle 1
- Data 4

+ Data 6
+ Data(copy of 6) 7
+ Handle 6
- Data 7
- Data 6

end
- Handle 4
- Handle 5
- Handle 3
- Handle 6

如您所见,Handle 被复制了多次。这是因为在调整它们所在的容器大小时需要复制它们。如果添加行

container.reserve(4);

就在打印“开始”的行之前,我想您会首先看到您期望看到的内容。也就是说,没有Handle 副本。

【讨论】:

    【解决方案2】:

    Handle(Data d) 按值获取Data 对象,因此您的Handle 构造函数中的d 实际上是您传递给Handle 构造函数的对象的副本。由于必须创建副本,因此也必须销毁副本。它的作用域是 Handle 构造函数的主体,所以它发生在“+ Handle”之后

    构造函数应该是这样的:

    Handle(const Data& d){
        // (upload data to video memory)
        cout<<"+ Handle"<<endl;
    }
    

    const 引用是为了防止不必要的复制。

    【讨论】:

    • 谢谢你的回答,它解释了额外的数据副本,但它没有解释给我带来麻烦的额外的句柄副本。
    猜你喜欢
    • 2016-09-09
    • 2020-03-05
    • 2010-12-25
    • 1970-01-01
    • 2014-09-17
    • 2022-01-23
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多