I got the follwing block of code:
#include <vector>
#include <iostream>
struct TestStruct {
bool wasCreated;
TestStruct() {
std::cout << "Default Constructor" << std::endl;
wasCreated = false;
}
~TestStruct() {
if (wasCreated) {
DoImportantStuff();
}
}
void Create() {
wasCreated = true;
}
// delete all copy stuff
TestStruct(const TestStruct&) = delete;
TestStruct& operator=(const TestStruct&) = delete;
// implement only move assignment & ctor
TestStruct(TestStruct&& other) {
std::swap(wasCreated, other.wasCreated);
}
TestStruct& operator=(TestStruct&& other) {
std::swap(wasCreated, other.wasCreated);
return *this;
}
// very important stuff
void DoImportantStuff() {
std::cout << "Do Important Stuff" << std::endl;
}
};
int main() {
std::vector<TestStruct> testVector;
testVector.emplace_back(TestStruct());
testVector.emplace_back(TestStruct());
std::cin.get();
}
This code leads to the output:
Default Constructor
Do Important Stuff
Default Constructor
Do Important Stuff
Do Important Stuff
Basicly I wanted to write a class, which owns memory but allocates this memory only when I call Create(). To avoid memory leaks and to avoid deleting not allocated memory I introduced wasCreated which will be only true when i call Create(). Every TestStruct should be saved in one vector. So in implemented move assigment & ctor and deleted both copy assigment & ctor.
Now it seems to me that the vector doenst call interally the default constructor of my TestStruct when its allocates new memory. Why is so and how do get the vector to call the default constructor on memory allocation? Do I need my own allocator?
Your problem is that your move constructor is implemented incorrectly. It swaps wasCreated between the newly created object and the one being moved from, but the variable in the newly created object has not been initialized yet (a default-constructed bool has an unknown value). So your temporary objects created with TestStruct() receive an uninitialized bool, which happens to be true in your case, hence the calls to DoImportantStuff() in their destructors.
So the move constructor should look something like this:
// implement only move assignment & ctor
TestStruct(TestStruct&& other) : wasCreated(other.wasCreated) {
other.wasCreated = false;
}
(You have moved ownership to the newly created object, the old one doesn't own anything anymore.)
Don't confuse the assignment operator with the constructor; they do different things. The assignment operator deals with two objects that are both already constructed; in the case of the constructor, the object being constructed is, well..., not constructed yet, so it doesn't have a valid state.
By the way, emplace_back() is pointless the way you're using it. Its purpose is to forward its arguments directly to the constructor of the object inside the vector. Since you have a default constructor (no arguments), the call should be:
testVector.emplace_back();
This will default-construct the TestStruct in place.
Now it seems to me that the vector doenst call interally the default constructor of my TestStruct when its allocates new memory.
A default-constructed vector has zero size, so there are no objects to construct.
If you want the vector to default-construct some objects, resize it.
Related
I have a class in my C++ code which has its own move constructor. A simplified version is shown here:
class myClass {
//In this example, myClass must manually manage allocating
//and freeing a memory buffer.
char *mem;
//...
//Regular constructor, copy constructor, etc
//...
myClass(myClass &&other) {
//Swap our memory pointer with other's memory pointer
char *tmp = other.mem;
other.mem = mem;
mem = tmp;
}
//...
//Destructor, other member functions, etc.
//...
}
In normal situations, this works fine. However, recently I needed to make a vector of these objects:
vector<myClass> v;
v.reserve(10); //Make space, but do not construct
v.push_back(myClass()); //Problem!
After getting a segfault and stepping through with gdb, I eventually discovered what should have been obvious: if you try to construct an object from an rvalue reference, this can result in using the move constructor on uninitialized memory.
How are you supposed to write a move constructor when it's possible that you're swapping garbage into the other class? Is there some way to detect this?
How are you supposed to write a move constructor when it's possible that you're swapping garbage into the other class? Is there some way to detect this?
An object that is not initialized holds an indeterminate value until assigned another value [basic.indet]/1. You're basically not allowed to do anything with an object holding an indeterminate value except for assigning it a proper value [basic.indet]/2. Since you're not even allowed to look at the value an object holds unless it has been initialized or assigned a value, there cannot possibly be a way to detect whether an object has been initialized just by looking at the object itself (because you're not allowed to even take a look). Thus, strictly speaking, you're actually not just "swapping garbage values into the other class", you're invoking undefined behavior. Garbage being swapped is just how that undefined behavior will typically manifest.
The solution to the problem is simple: Make sure that your pointer is always initialized to a valid value, e.g., nullptr:
class myClass {
//In this example, myClass must manually manage allocating
//and freeing a memory buffer.
char *mem = nullptr;
//...
//Regular constructor, copy constructor, etc
//...
myClass(myClass &&other) {
//Swap our memory pointer with other's memory pointer
char *tmp = other.mem;
other.mem = mem;
mem = tmp;
}
//...
//Destructor, other member functions, etc.
//...
}
Rather than implement the move constructor yourself, consider, e.g., just using a member of type std::unique_ptr and simply relying on the implicitly defined move constructor. For example:
class myClass
{
std::unique_ptr<char[]> mem;
// regular constructor, copy constructor, etc.
myClass(myClass&&) = default;
// other member functions, etc.
};
Don't swap the pointers in the constructor. That's not how you write move constructors. Swapping is for move-assignment, when both objects are live.
Constructors exist to initialize an object. As such, the memory they start with is always in the "uninitialized" state. So unless you initialize a member (or it has a default constructor that initializes it for you), the member's value will start uninitialized.
The correct way to handle this is just copy the pointer in the member initializer, then null out the other one.
myClass(myClass &&other) : mem(other.mem) {
other.mem = nullptr;
}
Or, with C++14 (and C++20 with a constexpr version), you can exchange the value:
myClass(myClass &&other)
: mem(std::exchange(other.mem, nullptr))
{}
struct foo{
int* i;
foo(): i(new int(42)){}
foo(const foo&) = delete;
foo(foo&&) = default;
~foo(){
std::cout << "destructor: i" << std::endl;
delete(i);
}
};
int main()
{
foo f;
auto sp_f = std::make_shared<foo>(std::move(f));
}
This is bad because it seems that the destructor of f will be called once it moves into the shared_ptr. The shared_ptr will have a deleted pointer and will delete it after it goes out of scope, which means the pointer will be deleted two times.
How do I avoid this problem?
You need to define the move constructor to prevent deletion from the moved-from object:
foo(foo&& f): i(f.i) {
f.i = nullptr;
}
Now when to destructor of the old object is run, it won't delete the i, since deleting a null pointer is a no-op.
You should also define a move assignment operator and delete the copy assignment operator too.
The rule of three is really the rule of five now. If you have a class that can be moved from, you should define the move semantics yourself (plus the copy, destructor, etc).
As to how to do that, to quote cppreference's page on std::move, "... objects that have been moved from are placed in a valid but unspecified state." The unspecified state is typically what the object would look like if it had been default initialized, or what would happen if the objects had swap called on them.
A straightforward way is, as #zenith has answered, is to have the move constructor (or assignment operator) set the original pointer to nullptr. This way the data isn't freed, and the original object is still in a valid state.
Another common idiom is, as mentioned, to use swap. If a class needs its own copy and move semantics, a swap method would be handy as well. The move constructor would delegate initialization to the default constructor, and then call the swap method. In a move assignment operator, just call swap. The object being moved into will get the resources, and the other object's destructor will free the original ones.
It would usually look like this:
struct Foo
{
void* resource; //managed resource
Foo() : resource(nullptr) {} //default construct with NULL resource
Foo(Foo&& rhs) : Foo() //set to default value initially
{
this->swap(rhs); //now this has ownership, rhs has NULL
}
~Foo()
{
delete resource;
}
Foo& operator= (Foo&& rhs)
{
this->swap(rhs); //this has ownership, rhs has previous resource
}
void swap(Foo& rhs) //basic swap operation
{
std::swap(resource, rhs.resource); //thanks #M.M
}
};
I wrote the following dummy class to understand how the copy constructor,the copy assignment operator and the destructor works:
#include <string>
#include <iostream>
class Box {
public:
// default constructor
Box(int i=10,const std::string &t=std::string()) : a(i),s(new std::string(t)) {}
// copy constructor
Box(const Box &other) { a=other.a; s=new std::string(*other.s); }
// copy assignment operator
Box &operator=(const Box &other) { a=other.a; s=new std::string(*other.s); }
// destructor
~Box() { std::cout<<"running destructor num. "<<++counter<<std::endl; }
int get_int() { return a; }
std::string &get_string() { return *s; }
private:
int a;
std::string *s;
static int counter;
};
int Box::counter=0;
I'm using this class type in my code to test how it works but I was thinking about the implications in destroying objects which have a member of built-in pointer type:
#include "Box.h"
using namespace std;
int main()
{
Box b1;
Box b2(2,"hello");
cout<<b1.get_int()<<" "<<b1.get_string()<<endl;
cout<<b2.get_int()<<" "<<b2.get_string()<<endl;
Box b3=b1;
Box b4(b2);
cout<<b3.get_int()<<" "<<b3.get_string()<<endl;
cout<<b4.get_int()<<" "<<b4.get_string()<<endl;
b1=b4;
cout<<endl;
cout<<b1.get_int()<<" "<<b1.get_string()<<endl;
{
Box b5;
} // exit local scope,b5 is destroyed but string on the heap
// pointed to by b5.s is not freed (memory leak)
cout<<"exiting program"<<endl;
}
This pointer is initialized in the constructor to point to a (always new) dynamically allocated memory on the free store. So,when the destructor is called, members of the object to be destroyed are destroyed in reverse order. Is it right in this case, that only the int and the pointer objects are destroyed, and I end up having a memory leak (the string on the heap is not freed)?
Moreover, defining this copy assignment operator, do I have a memory leak every time I assign an object (the pointer points to a new object on the heap and the former is lost isn't it?) ?
Each time you call new, you have to delete it (except are shared pointers).
So you have to delete the string in the destructor.
The assignment operator works on an existing instance, so you already created s and do not have to create a new string for s.
the destructor destructs its members. Since a pointer is like a int, only the variable holding the address is destructed, not the object it is pointing to.
So yeah, you will have a memory leak in each object and everytime you use the assignment operator the way you designed your class.
Keep in mind allocation happens on base construction, copy construction and surprisingly conditionally on assignment
Deallocation happens in the destructor and conditionally on assignment
The condition to watch for is:
x = x;
So your code can be changed to the following pattern (in cases where you are not able to use the preferred appropriate smart pointer)
Box(int i=10,const std::string &t=std::string()) : a(i),s(new std::string(t)) {}
// copy constructor
Box(const Box &other) { cp(other); }
// copy assignment operator
Box &operator=(const Box &other) {
if (&other != this) // guard against self assignment
{
rm();
cp(other);
}
return *this;
}
// destructor
~Box() { rm(); }
private:
void cp(const Box &other) {a=other.a; s=new std::string(*other.s);
void rm() {
std::cout<<"running destructor num. "<<++counter<<std::endl;
delete s; // prevents leaks
}
One possible way to deal with unnamed dynamically allocated members is to save them in a container every time they are created (in an object, function, etc), and then to run a for loop in your destructor with a delete statement followed by the elements of the container.
You can do this with a vector:
vector <string*> container;
and you can use it as follows:
// define it in the private members of your class
vector <string*> container;
// use it when you create the string
container.push_back(new dynamicallyAllocatedObject);
// free memory in the destructor
for(auto it = container.begin(); it != container.end(); ++it) delete *it;
In order to avoid duplication of elements, I'm building a class that holds elements and provide an acces to them.
My elements (DynLibrary) are movable but not copyable
class DynLibrary
{
public:
DynLibrary() : _handle(nullptr) {}
DynLibrary(const std::string& path) { DynLibrary::open(path); }
DynLibrary(const DynLibrary&) = delete;
DynLibrary(DynLibrary&&) = default;
~DynLibrary() { DynLibrary::close(); }
...
}
Those object are allocated in an unordered_map which key is the path that generated them.
I'm allocation them that way
class DynAllocator
{
public:
DynLibrary& library(const std::string& f)
{
if (_handles.find(f) == _handles.end())
{
std::cout << "#Emplace" << std::endl;
_handles.emplace(f, DynLibrary(f));
}
std::cout << "#Return" << std::endl;
return _handles.at(f);
}
private:
std::unordered_map<std::string, DynLibrary> _handles;
};
However when calling DynAllocator::library I get the following output:
#Emplace
close 0x1dfd1e0 // DynLibrary destructor
#Return
Which means that the object which is inserted has somehow been copied and the destructor of the copy just invalidated my object (calling dlclose with my handler)
Is my movable but not copyable approach of DynLibrary ok ?
How can I insert an instance of DynLibrary if my unordered_map without copy ?
Please note that I know how to do that using pointers / smart pointers (std::unique_ptr) but that i'd like to avoid them at all cost !
Which means that the object which is inserted has somehow been copied and the destructor of the copy just invalidated my object
No, that's not what that means. DynLibrary has a deleted copy constructor, so the code would not compile if that constructor were somehow chosen through overload resolution.
_handles.emplace(f, DynLibrary(f));
What's happening on the line above is you're creating a temporary DynLibrary object which is then move constructed into the unordered_map. If you wish to avoid this move construction, use std::piecewise_construct instead.
_handles.emplace(std::piecewise_construct,
std::forward_as_tuple(f),
std::forward_as_tuple(f));
Now you're directly constructing the DynLibrary object within the unordered_map and bypassing creation of the temporary.
As T.C. comments, the piecewise construction constructor is not necessary in this case because DynLibrary has a non-explicit converting constructor. You can achieve the same effect as above with
_handles.emplace(f, f);
Consider for example a template container class that holds a buffer that is allocated on the heap:
T *_buffer = new T[SIZE]
Just a simple pointer to c array of type T.
This class is templated. However I am having issues with performing a deep copy of an object into my buffer.
In my unit test, i set up a test class:
class test
{
public:
int* _ptrInt;
test() {_ptrInt = nullptr;}
test(const int i)
{
_ptrInt = new int;
*_ptrInt = i;
}
test(const test& other)
{
_ptrInt = new int;
*_ptrInt = *other._ptrInt;
}
~test()
{
delete _ptrInt;
}
};
on my container I call set, passing a temporary as the data:
container.set(0, test(5));
// destructor called on copy immediately after statement, invalidating deep copy in buffer
void set (const int& index, const T& data)
{
int i = realign(index);
T copy = data;
_buffer[i==SIZE?i-1:i] = copy; // ternary statement and index work
}
however, _buffer takes copy as a reference, the moment copy goes out of scope, it deletes the same pointer that is held in the _buffer. I am trying to force the _buffer to assign by value. But I have had no luck.
memcpy still copies the pointers to point to the same address
test copy constructor is correctly called
move semantics would require class to have move constructor
std::vector somehow implements this to copy correctly, whether its T/T*, heap/stack, with/without move constructor, so I know it must be possible
Is there a way I can assign by value to the _buffer on the heap?
You are "assigning by value." However, your test class doesn't implement the assignment operator operator=, so the assignment invokes the compiler-generated default assignment operator which simply copies member-by-member. Hence the problems with shallow assignment.
Also, your copy constructor will explode if other._ptrInt is nullptr.