Why std::move doesn't avoid a second destruction? - c++

I know ordinary std::vector::push_back() will copy the object. I hope this code would only destruct a only once, using std::move() and A(A&&) noexcept to avoid copying. But it doesn't seem to work.
Is there any way that I can construct an object before push_back() and move it into a vector perfectly?
#include <bits/stdc++.h>
using namespace std;
class A {
public:
A() { std::cout << "construct" << this << '\n'; }
A(A&&) noexcept { std::cout << "move" << this << "\n"; }
A(const A&) = delete;
~A() { std::cout << "destruct" << this << '\n'; }
};
std::vector<A> as;
void add(A&& a) {
std::cout << "add 1\n";
as.push_back(std::move(a));
std::cout << "add 2\n";
}
int main() {
add(A());
std::cout << "main2\n";
return 0;
}
Output:
construct0x16d20b1fb
add 1
move0x151e068b0
add 2
destruct0x16d20b1fb
main2
destruct0x151e068b0
I hope this code would only destruct a only once, using std::move() and A(A&&) noexcept to avoid copying.

Using Move constructor perverts copying but doesn't prevent creating new objects, you are creating an inline object with default constructor "cout : construct0x16d20b1fb" and then from that object's data your going to create a 'NEW' object and calling move constructor will
transform the ownership of the object's data/resources to the new object that is being 'Constructed' so that explain the "cout : move0x151e068b0" then the line is finished so your inline object is destroyed "cout " destruct0x16d20b1fb" then your program finishes "cout : destruct0x151e068b0" your object that made via move constructor is destroyed.
you are expecting the behavior of pointers from your vector of "Objects" which is supposed to hold actual objects not pointer to other objects so it needs to containt objects and each of objects that are in that vector has a different address, unless you create your object and use a vector pointers to objects.
By the way std::move is but a cast, it casts to rvalue references so when you are capturing with rvalue reference there's no need to cast it again, you should be using it like this:
A a_object1;
vector.push_back(std::move(a_object1));
and it will use the move constructor. although in this case it will cast it implicitly because you are capturing only by rvalue reference in add function unless you add an overload to this function that also takes reference there's no need.

Related

c++ - Destructor called on assignment

I'm still learning the basics of c++ so I may not have had the correct vocabulary to find the answer to my question but I couldn't find this mentioned anywhere.
If I have a class with a constructor and destructor why does the destructor get called on the new data when I am assigning to the class?
For example:
#include <iostream>
class TestClass {
public:
int* some_data;
TestClass() {
std::cout << "Creating" << std::endl;
some_data = (int*)malloc(10*sizeof(int));
}
~TestClass() {
std::cout << "Deconstructing" << std::endl;
free(some_data);
}
TestClass(const TestClass& t) : some_data{t.some_data} {
std::cout << "Copy" << std::endl;
}
};
int main() {
TestClass foo;
std::cout << "Created once" << std::endl;
foo = TestClass();
std::cout << "Created twice" << std::endl;
}
which prints:
Creating
Created once
Creating
Deconstructing
Created twice
Deconstructing
free(): double free detected in tcache 2
Aborted (core dumped)
So after following this in the debugger it appears the deconstructor is called on the newly created data which is confusing to me. Shouldn't the original data be freed once and then at the end of execution the new data should be freed? It seems like the original data is never freed like this.
Your object owns a raw pointer to allocated memory, but does not implement a proper copy constructor that makes an allocation and copies the data behind the pointer. As written, when you copy an object, the pointer is copied, such that now two objects point to the same address (and the old one that the just-assigned-to object is leaked.)
When the temporary goes out of scope, it deletes its pointer but the copy (foo) still points to it. When foo goes out of scope, it deletes the same pointer again, causing this double free error you're seeing.
If you need to write a destructor to clean up, you almost always need to also provide copy and assignment operations, or disable them.
SUGGESTIONS:
hold the pointer in a std::unique_ptr which will fail to compile if you try to copy it. This forces you to deal with the issue. Also, malloc and free are mainly for C or low-level C++ memory management. Consider using new and delete for allocations instead. (unique_ptr uses delete by default, not free, and you must not mix them.)
alternately, delete the copy constructor and assignment operator
also, consider when you want to move from an xvalue (temporary or moved lvalue), you can pilfer the allocation from the right-hand-side. So this class is a good candidate for move constructor and move assignment.
Most of the comments and a few details more in code:
#include <iostream>
#include <array>
#include <memory>
class TestClass
{
// members of a class should not be public
private:
// TestClass owns the data, this is best modeled
// with a unique_ptr. std::array is a nicer way of
// working with arrays as objects (with size!)
std::unique_ptr<std::array<int, 10>> some_data;
public:
TestClass() :
some_data{ std::make_unique<std::array<int,10>>() }
{
std::cout << "Creating" << std::endl;
// set everything in the array to 0
std::fill(some_data->begin(), some_data->end(), 0);
}
~TestClass()
{
std::cout << "Destructing" << std::endl;
// no need to manually delete a std::unique_ptr
// its destructor will free the memory
// and that will be called as part of this destructor
}
TestClass(const TestClass& t) :
// when you copy a class the copy should have its
// own copy of the data (to avoid deleting some data twice)
// or you must chose shared ownership (lookup std::shared_ptr)
some_data{ std::make_unique<std::array<int,10>>() }
{
std::cout << "Copy" << std::endl;
// copy data from t to this instances array
// (note this would not have been necessary
// it your array was just a non-pointer member,
// try that for yourself too.)
std::copy(some_data->begin(), some_data->end(), t.some_data->begin());
}
TestClass(TestClass&& rhs) :
some_data{ std::move(rhs.some_data) } // transfer ownership of the unique_pointer to this copy
{
std::cout << "Move" << std::endl;
}
// Important this assignement operator is used in your original code too
// but you couldn't see it!
TestClass& operator=(const TestClass& t)
{
some_data = std::make_unique<std::array<int, 10>>();
std::copy(some_data->begin(), some_data->end(), t.some_data->begin());
std::cout << "Assignment" << std::endl;
return *this;
}
};
int main()
{
TestClass foo;
std::cout << "Created once" << std::endl;
foo = TestClass();
std::cout << "Created twice" << std::endl;
TestClass bar{ std::move(foo) };
std::cout << "Moved" << std::endl;
}

Strange side effect from a copy constructor

This simple code:
#include <iostream>
#include <vector>
struct my_struct
{
int m_a;
my_struct(int a) : m_a(a) { std::cout << "normal const " << m_a << std::endl; }
my_struct(const my_struct&& other) : m_a(other.m_a) { std::cout << "copy move " << other.m_a << std::endl; }
my_struct(const my_struct &other) : m_a(other.m_a) { std::cout << "copy const " << other.m_a << std::endl; }
};
class my_class
{
public:
my_class() {}
void append(my_struct &&m) { m_vec.push_back(m); }
private:
std::vector<my_struct> m_vec;
};
int main()
{
my_class m;
m.append(my_struct(5));
m.append(std::move(my_struct(6)));
}
produces this output:
normal const 5
copy const 5
normal const 6
copy const 6
copy const 5
The first call to append creates the object, and push_back creates a copy. Likewise, the second call to append creates the object, and push_back creates a copy. Now, a copy constructor of the first object is mysteriously called. Could someone explain me what happens? It looks like a strange side effect...
Now, a copy constructor of the first object is mysteriously called. Could someone explain me what happens? It looks like a strange side effect...
When you call push_back on std::vector, vector may need to grow it's size as stated in the cppreference:
If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.
You can use reserve before pushing anything to your vector. Try this:
class my_class
{
public:
my_class()
{
m_vec.reserve(10); // Use any number that you want.
}
void append(my_struct &&m) { m_vec.push_back(m); }
private:
std::vector<my_struct> m_vec;
};
Few other issues with your program:
You need to fix signature of your move constructor as move constructor requires rvalue reference (more specifically, xvalue or prvalue). It should like this:
my_struct(my_struct&& other) noexcept : m_a(other.m_a)
{
std::cout << "copy move " << other.m_a << std::endl;
}
noexcept is required as we need to inform C++ (specifically std::vector) that move constructor and destructor does not throw, using noexcept. Then the move constructor will be called when the vector grows. See this.
The method append should be:
void append(my_struct &&m)
{
m_vec.push_back(std::move(m));
}
To know why we need to use std::move on rvalue reference, see this Is an Rvalue Reference an Rvalue?. It says:
Things that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue.
If you don't use std::move, then copy constructor would be called.
That's just how std::vector works!
When you call push_back(), the underlying array needs to grow to make room for the new element.
So internally, a new larger array is allocated and all the elements of the previous smaller array are copied into the freshly created array. This also comes with some overhead. Now, you can use some techniques to optimize away the copies.
If you have an idea of how large the array could grow, you can use the reserve() method to ensure that no resizing will occur upto that many locations.
vct.reserve(5)
This is will ensure that no resizing will occur until 5 elements.
Also, you can use the emplace_back() function to avoid an additional copy. It constructs the object in place. Simply pass the constructor parameters of the object to emplace_back()

How to pass the ownership of an object to the outside of the function

Is there any method that can pass the ownership of an object created in the function on the stack memory to the outside of the function without using copy construction?
Usually, compiler will automatically call destruction to the object on the stack of a function. Therefore, if we want to create an object of a class(maybe with some specific parameters), how can we avoid wasting lots of resources copying from temp objects?
Here is one common case:
while(...){
vectors.push_back(createObject( parameter ));
}
So when we want to create objects in a iteration with some parameters, and push them into a vector, normal value passed way will take a lot of time copying objects. I don't want to use pointer and new objects on the heap memory as user are likely to forget delete them and consequently cause memory leak.
Well, smart pointer maybe a solution. But..less elegent, I think. hhhh
Is there any way of applying rvalue reference and move semantics to solve this problem?
Typically, returning an object by value will not copy the object, as the compiler should do a (named) return value optimization and thereby elide the copy.
With this optimization, the space for the returned object is allocated from the calling context (outer stack frame) and the object is constructed directly there.
In your example, the compiler will allocate space for the object in the context where createObject() is called. As this context is an (unnamed) parameter to the std::vector<T>.push_back() member function, this works as an rvalue reference, so the push_back() by-value will consume this object by moving (instead of copying) it into the vector. This is possible since if the generated objects are movable. Otherwise, a copy will occur.
In sum, each object will be created and then moved (if moveable) into the vector.
Here is a sample code that shows this in more detail:
#include <iostream>
#include <string>
#include <vector>
using Params = std::vector<std::string>;
class Object
{
public:
Object() = default;
Object(const std::string& s) : s_{s}
{
std::cout << "ctor: " << s_ << std::endl;
}
~Object()
{
std::cout << "dtor: " << s_ << std::endl;
}
// Explicitly no copy constructor!
Object(const Object& other) = delete;
Object(Object&& other)
{
std::swap(s_, other.s_);
std::cout << "move: traded '" << s_ << "' for '" << other.s_ << "'" << std::endl;
}
Object& operator=(Object other)
{
std::swap(s_, other.s_);
std::cout << "assign: " << s_ << std::endl;
return *this;
}
private:
std::string s_;
};
using Objects = std::vector<Object>;
Object createObject(const std::string& s)
{
Object o{s};
return o;
}
int main ()
{
Objects v;
v.reserve(4); // avoid moves, if initial capacity is too small
std::cout << "capacity(v): " << v.capacity() << std::endl;
Params ps = { "a", "bb", "ccc", "dddd" };
for (auto p : ps) {
v.push_back(createObject(p));
}
return 0;
}
Note that the class Object explicitly forbids copying. But for this to work, the move constructur must be available.
A detailed summary on when copy elision can (or will) happen is available here.
Move semantics and copy elison of vectors should mean the elements of the local std::vector are in fact passed out of the object and into your local variable.
Crudely you can expect the move constructor of std::vector to be something like:
//This is not real code...
vector::vector(vector&& tomove){
elems=tomove.elems; //cheap transfer of elements - no copying of objects.
len=tomove.elems;
cap=tomove.cap;
tomove.elems=nullptr;
tomove.len=0;
tomove.cap=0;
}
Execute this code and notice the minimum number of objects are constructed and destructed.
#include <iostream>
#include <vector>
class Heavy{
public:
Heavy(){std::cout<< "Heavy construction\n";}
Heavy(const Heavy&){std::cout<< "Heavy copy construction\n";}
Heavy(Heavy&&){std::cout<< "Heavy move construction\n";}
~Heavy(){std::cout<< "Heavy destruction\n";}
};
std::vector<Heavy> build(size_t size){
std::vector<Heavy> result;
result.reserve(size);
for(size_t i=0;i<size;++i){
result.emplace_back();
}
return result;
}
int main() {
std::vector<Heavy> local=build(5);
std::cout<<local.size()<<std::endl;
return 0;
}
Move semantics and copy elison tend to take care of this problem C++11 onwards.
Expected output:
Heavy construction
Heavy construction
Heavy construction
Heavy construction
Heavy construction
5
Heavy destruction
Heavy destruction
Heavy destruction
Heavy destruction
Heavy destruction
Notice that I reserved capacity in the vector before filling it and used emplace_back to construct the objects straight into the vector.
You don't have to get the value passed to reserve exactly right as the vector will grow to accommodate values but it that will eventually lead to a re-allocation and move of all the elements which may be costly depending on whether you or the compiler implemented an efficient move constructor.

Reference binding and the copy constructor/move constructor

If I have a function like so:
int foo(std::vector<int>* integer1ArrayIn, * integer2ArrayIn) {
std::vector<int>& integer1Array = *integer1ArrayIn;
std::vector<int>& integer2Array = *integer2ArrayIn;
}
Will the reference integer1Array be calling a copy constructor/move constructor to copy over the elements of the passed in parameter?
Does binding a reference to a dereferenced pointer call the copy constructor?
In what circumstances does reference binding invoke a copy constructor?
Can someone explain what happens as this code executes in memory?
Thank you
No.
No, but it would crash badly if it was nullptr. Consider passing by reference whenever a parameter HAS to be there, and pass by pointer when a parameter MIGHT be there (but always verify for nullptr too!).
Whenever the l-value (in this case integer1Array and integer2Array) are pointers or references, it will never call copy/move constructor.
if you had std::vector integer1Array = *integer1ArrayIn it would effectively make a copy.
You can use Jonas's answer to play around with and see for yourself :)
1) No, there are no copies made. You can test it with a small program, like this.
#include <iostream>
struct foo
{
foo() { std::cout << "Constructor" << std::endl; }
foo(const foo&) { std::cout << "Copy constructor" << std::endl; }
foo& operator=(const foo&) { std::cout << "Copy assignment operator" << std::endl; }
};
int main() {
foo* A = new foo;
foo& B = *A;
delete A;
}
2) Beware of nullptrs! Otherwise all is fine.
3) Never (see answer by AlexG)
4) Not sure what you mean by "code executes in memory", since code is not executed in memory. If you mean what happens to the memory when the program is executed, then that's another story

STL vector allocations

I was wondering why the vector templates perform two allocations, when only one seems to be
necessary.
For example this:
#include <vector>
#include <iostream>
class A {
public:
A(const A &a) {
std::cout << "Calling copy constructor " << this << " " << &a << "\n";
}
A() {
std::cout << "Calling default constructor " << this << "\n";
}
~A() {
std::cout << "Calling destructor " << this << "\n";
}
};
int main(int argc, char **argv)
{
std::vector <A> Avec;
std::cout << "resize start\n";
Avec.resize(1);
std::cout << "resize end\n";
return 0;
}
Outputs:
resize start
Calling default constructor 0x7fff9a34191f
Calling copy constructor 0x1569010 0x7fff9a34191f
Calling destructor 0x7fff9a34191f
resize end
It isn't performing two allocations, it is creating an object by the default constructor to pass into resize, then copying that object into the new position, then destructing the argument.
If you look at the arguments to resize:
void resize(n, t = T())
It has as a defaulted argument a default constructed object of type T (this is the default constructor being called in your output). Then, within the function, it copies this into the correct position (this is the copy constructor). After the resize function ends, destroys the argument (the destructor call in the output).
Here is a guess:
The compiler re-ordered the initial allocation of Avec until after the "resize start"
The vector is initially allocated with 0 elements
The resize gets the new element filled with a default A (which was achieved by createing a default A, copying it into the vector, and deleting the temporary.
If you initialize objects that way the vector template creates objects by making a copy.
If you don't want to call copy constructor you should make :
vector<A*> Avec;
avec.push_back(new A());
http://www.cplusplus.com/reference/stl/vector/vector/