While debugging zpaq I found that g++ and VC++ treat std::vector differently when the element type has a constructor, such as a vector<string>. The following program creates a vector of 3 elements, then resizes it to 5, and then 1. Each element has a std::string and a pointer to allocated memory. I wrote a copy constructor, assignment operator, and destructor that are identical to the defaults supplied. (You get the same behavior if you remove them). The default is to copy, assign, or destroy each of the members of the object. (This is incorrect for pointers. Nevertheless I did it this way to see what was happening).
The declaration vector<T> v(n) creates n elements of type T. g++ constructs a single temporary object, then makes n bitwise copies, and destroys the temporary. This is obviously incorrect if T allocates memory because all of the objects point to the same memory. All of the strings point to the same memory, but it works because g++ uses copy-on-write. VC++ creates a temporary object once for each element, copies it to the vector, and destroys the temporary. This is correct, but inefficient. When a string is copied in VC++, the value is copied to new memory.
When a vector is enlarged using resize(), it must allocate new elements and copy over the old ones to the new array. Both do this correctly using the copy constructor. New elements are initialized in the same way as when a vector is created.
When a vector is resized to a smaller size, neither g++ nor VC++ free any memory. Both correctly call the destructor on the removed elements. g++ inexplicably creates a temporary object (as if to initialize new elements) and then destroys it without using it.
In another experiment (not shown), I found that with a series of push_back()s, that when g++ needs to enlarge the vector, it doubles the reserved space. VC++ allocates in smaller chunks which is more memory efficient but becomes slow when the vector gets large.
Code:
#include <stdio.h>
#include <string>
#include <vector>
using namespace std;
struct T {
string s;
char* p;
T(): s(1<<24, 0), p(new char[1<<24]) {printf("constructor\n");}
~T() {printf("destructor\n");}
T(const T& x): s(x.s), p(x.p) {printf("copy constructor\n");}
T& operator=(const T& x) {s=x.s, p=x.p; printf("operator=\n"); return *this;}
};
int main() {
printf("\n string copy constructor behavior\n");
string s="hello";
string t=s;
printf(" s=%s %p t=%s %p\n", s.c_str(), s.c_str(), t.c_str(), t.c_str());
t.resize(4);
printf("resized: s=%s %p t=%s %p\n\n", s.c_str(), s.c_str(), t.c_str(), t.c_str());
vector<T> v(3);
printf(" Create with size 3 at %p\n", &v[0]);
for (unsigned i=0; i<v.size(); ++i)
printf("%d %d %p %p\n", i, v[i].s.size(), v[i].s.c_str(), v[i].p);
v.resize(5);
printf("\n resize 3 to 5 at %p\n", &v[0]);
for (unsigned i=0; i<v.size(); ++i)
printf("%d %d %p %p\n", i, v[i].s.size(), v[i].s.c_str(), v[i].p);
printf("\n resize 5 to 1 at %p\n", &v[0]);
v.resize(1);
for (unsigned i=0; i<v.size(); ++i)
printf("%d %d %p %p\n", i, v[i].s.size(), v[i].s.c_str(), v[i].p);
return 0;
}
Output:
Code:
C:\res\jidac>g++ -O3 a.cpp -o a.exe
C:\res\jidac>a
string copy constructor behavior
s=hello 002E3474 t=hello 002E3474
resized: s=hello 002E3474 t=hell 002E3494
constructor
copy constructor
copy constructor
copy constructor
destructor
Create with size 3 at 002E34A8
0 16777216 0098002C 01990020
1 16777216 0098002C 01990020
2 16777216 0098002C 01990020
constructor
copy constructor
copy constructor
copy constructor
copy constructor
copy constructor
destructor
destructor
destructor
destructor
resize 3 to 5 at 002E34C8
0 16777216 0098002C 01990020
1 16777216 0098002C 01990020
2 16777216 0098002C 01990020
3 16777216 029A002C 039B0020
4 16777216 029A002C 039B0020
resize 5 to 1 at 002E34C8
constructor
destructor
destructor
destructor
destructor
destructor
0 16777216 0098002C 01990020
destructor
C:\res\jidac>cl /O2 /EHsc a.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
a.cpp
Microsoft (R) Incremental Linker Version 10.00.30319.01
Copyright (C) Microsoft Corporation. All rights reserved.
/out:a.exe
a.obj
C:\res\jidac>a
string copy constructor behavior
s=hello 002CFEE0 t=hello 002CFEC4
resized: s=hello 002CFEE0 t=hell 002CFEC4
constructor
copy constructor
destructor
constructor
copy constructor
destructor
constructor
copy constructor
destructor
Create with size 3 at 006D19F8
0 16777216 02900020 018F0020
1 16777216 04920020 03910020
2 16777216 06940020 05930020
copy constructor
copy constructor
copy constructor
destructor
destructor
destructor
constructor
copy constructor
destructor
constructor
copy constructor
destructor
resize 3 to 5 at 006D1A60
0 16777216 008E0020 018F0020
1 16777216 07950020 03910020
2 16777216 08960020 05930020
3 16777216 06940020 04920020
4 16777216 0A980020 09970020
resize 5 to 1 at 006D1A60
destructor
destructor
destructor
destructor
0 16777216 008E0020 018F0020
destructor