I'm doing an exercise from C++ Primer 5th Edition, which goes like:
Exercise 13.50: Put print statements in the move operations in your String class and rerun the program from exercise 13.48 in § 13.6.1 (p. 534) that used a vector to see when the copies are avoided.(P.544)
The String
is a class for practise which behaves like a std::string
without using any template. The String.h
file:
class String
{
public:
//! default constructor
String();
//! constructor taking C-style string i.e. a char array terminated with'\0'.
explicit String(const char * const c);
//! copy constructor
explicit String(const String& s);
//! move constructor
String(String&& s) noexcept;
//! operator =
String& operator = (const String& rhs);
//! move operator =
String& operator = (String&& rhs) noexcept;
//! destructor
~String();
//! members
char* begin() const { return elements; }
char* end() const { return first_free; }
std::size_t size() const {return first_free - elements; }
std::size_t capacity() const {return cap - elements; }
private:
//! data members
char* elements;
char* first_free;
char* cap;
std::allocator<char> alloc;
//! utillities for big 3
void free();
};
Implementation for default, copy and move constructor from String.cpp
:
//! default constructor
String::String():
elements (nullptr),
first_free (nullptr),
cap (nullptr)
{}
//! copy constructor
String::String(const String &s)
{
char* newData = alloc.allocate(s.size());
std::uninitialized_copy(s.begin(), s.end(), newData);
elements = newData;
cap = first_free = newData + s.size();
std::cout << "Copy constructing......\n";
}
//! move constructor
String::String(String &&s) noexcept :
elements(s.elements), first_free(s.first_free), cap(s.cap)
{
s.elements = s.first_free = s.cap = nullptr;
std::cout << "Move constructing......\n";
}
Main.cpp
:
int main()
{
std::vector<String> v;
String s;
for (unsigned i = 0; i != 4; ++i)
v.push_back(s);
return 0;
}
The output:
Copy constructing......
Copy constructing......
Copy constructing......
Copy constructing......
Copy constructing......
Copy constructing......
Copy constructing......
As can be seen, the move constructor was not called at all. Why wasn't the move constructor called when the vector allocating more memory?
Update:
The info of compiler:
gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-1ubuntu1)
main.cpp
with printing capacity and its ouput:
int main()
{
std::vector<String> v;
String s;
for (unsigned i = 0; i != 4; ++i)
{
std::cout << v.capacity() << "\n";
v.push_back(s);
}
return 0;
}
Output:
0
Copy constructing......
1
Copy constructing......
Copy constructing......
2
Copy constructing......
Copy constructing......
Copy constructing......
4
Copy constructing......
I reproduce with gcc 4.7.1 on MinGW...
Adding
~String() noexcept
solves the issue...