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......
Move constructor moves the resources in the heap, i.e., unlike copy constructors which copy the data of the existing object and assigning it to the new object move constructor just makes the pointer of the declared object to point to the data of temporary object and nulls out the pointer of the temporary objects.
It's faster because moving allows the source to be left in a invalid state, so you can steal it's resources. For example, if a object holds a pointer to a large block of allocated memory, a move can simply steal the pointer while a copy must allocate its own memory and copy the whole memory block.
If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then: No move constructor is automatically generated. No move-assignment operator is automatically generated.
In C++, a Copy Constructor may be called for the following cases: 1) When an object of the class is returned by value. 2) When an object of the class is passed (to a function) by value as an argument. 3) When an object is constructed based on another object of the same class.
I reproduce with gcc 4.7.1 on MinGW...
Adding ~String() noexcept
solves the issue...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With