When writing some code that loads a (part of) a datastructure into graphics memory, I was surprised by this unexpected behaviour.
I've simplified the code to the following:
#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;
}
what I expected:
start
+ Data
+ Handle
- Data
+ Data
+ Handle
- Data
+ Data
+ Handle
- Data
+ Data
+ Handle
- Data
end
-Handle
-Handle
-Handle
-Handle
what I got:
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
Can anyone explain this behaviour?
This modification of your code may help you better see what's happening.
#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;
}
What it does is to keep track of each allocation and deallocation with an integer to uniquely identify each. Copy constructors are also explicitly added. When I run this on my system I get the following:
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
As you can see, the Handle
is copied multiple times. This is because they need to be copied when the container they're in is resized. If you add the line
container.reserve(4);
just before the line that prints "start", I think you'll see what you expected to see in the first place. That is, no Handle
copies.