Let's say that I have some arbitrary class, A:
class A {
//... stuff
};
I want to call into an external API that takes in a shared pointer to some type, like so (I cannot change this interface):
//...much later
void foo(std::shared_ptr<A> _a){
//operate on _a as a shared_ptr
}
However, in the (legacy) code I'm working with, the class A instance I'm working with is allocated on the stack (which I cannot get around):
A a;
//...some stuff on a
//Now time to call foo
On top of this, an instance of class A is quite large, on the order of 1 GB per instance.
I know I could call
foo(std::make_shared<A> a);
but that would allocate memory for a copy of A, which I would really like to avoid.
Question
Is there a way to hack together some call to std::make_shared (possibly with move semantics) so that I am not forced to allocate memory for another instance of class A?
I've tried something like this:
foo(std::make_shared<A>(std::move(a)));
But from what I can tell, a new instance of A is still created.
Example code
#include <iostream>
#include <memory>
using namespace std;
class A{
public:
A(int _var=42) : var(_var){cout << "Default" << endl;}
A(const A& _rhs) : var(_rhs.var){cout << "Copy" << endl;}
A(A&& _rhs) : var(std::move(_rhs.var)){cout << "Move" << endl;}
int var;
};
void foo(std::shared_ptr<A> _a){
_a->var = 43;
cout << _a->var << endl;
}
int main() {
A a;
cout << a.var << endl;
foo(std::make_shared<A>(std::move(a)));
cout << a.var << endl;
a.var = 44;
foo(std::make_shared<A>(std::move(a)));
cout << a.var << endl;
return 0;
}
Output:
Default 42 Move 43 42 Move 43 44
This is possible with the shared_ptr constructor that allows for an "empty instance with non-null stored pointer":
A x;
std::shared_ptr<A> i_dont_own(std::shared_ptr<A>(), &x);
(It's "overload (8)" on the cppreference documentation.)
If you know that shared pointer you pass to foo() will not get stored, copied etc, ie will not outlive your object you can make std::shared_ptr pointed to object on the stack with empty deleter:
void emptyDeleter( A * ) {}
A a;
foo( std::shared_ptr<A>( &a, emptyDeleter ) );
Again you need to make sure that shared pointer or it's copy will not outlive the object and well document this hack.
Assuming class A supports move semantics, do this:
std::shared_ptr<A> newA = make_shared<A> (std::move (_a));
Do not use _a anymore, use only newA. You can now pass newA to the function.
If class A does not support move semantics, there is no safe/sane way to do this. Any hack will only happen to work, and may break in the future. If you control enough of the class code, you may be able to add support for move semantics.
But from what I can tell, a new instance of A is still created.
Why do you care? What you're trying to avoid is copying all the data in the instance, and this does that.
The point of move semantics is to move the data from one instance to another without having to do an allocate/copy/free. Of course, this makes the original instance "empty", so don't use that anymore.
Related
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;
}
I am struggling to understand the difference in behaviour of a raw pointer and a unique_ptr. I have class A with a variable x and class B with a pointer to an instance of A:
class A
{
public:
int x;
};
A::A(int y) : x(y)
{
}
class B
{
public:
B(A &);
A *p;
};
B::B(A &a)
{
p = &a;
}
This behaves as I would expect:
int main()
{
A a(2);
B b(a);
cout << a.x << " " << b.p->x << endl;
a.x = 4;
cout << a.x << " " << b.p->x << endl;
}
gives
2 2
4 4
Changing the raw pointer to a std::unique_ptr gives a different result:
class A
{
public:
int x;
};
A::A(int y) : x(y)
{
}
class B
{
public:
B(A &);
std::unique_ptr<A> p;
};
B::B(A &a)
{
p = std::make_unique<A>(a);
}
gives
2 2
4 2
Have I fundamentally misunderstood something about unique_ptrs?
make_unique creates a fresh object, one that that unique_pt has exclusive access to. So in the second example you have two objects, not one and when you set change the value of a.x in the first object it doesn't effect the other object held by the unique_ptr.
A unique pointer needs to own whatever it points to. Your code can be made to work - just substituting unique_ptr type and leaving everything else unchanged (no make_unique). But it will have undefined behavior, since you’ll create a unique pointer to an object that is owned elsewhere.
To compare apples to apples, the raw pointer code should read p=new A(a);. That’s what make_unique does.
Try reading the following expression from the smart pointer version:
std::make_unique<A>(a);
"make a unique A from a" (no mention of pointers!)
The result is a unique_ptr, but when reading the expression, read it as making (an object whose type is) the template parameter. The function parameters are parameters to the constructor. In this case, you are making an A object from an A object, which pulls in the copy constructor.
Once you understand that the smart pointer version is making a new A object (and your raw pointer version does not), your results should make sense.
The "unique" in "unique A" might be tricky to understand. Think of it as an object that no one else can lay claim to. It might be a copy of another object, but, taking the role of the unique_ptr, it is your copy, your responsibility to clean up after, and no one else's. Your preciousss, which you will not share (c.f. std::make_shared).
Note that a local variable (like the a in the main function) is the responsibility of the compiler, so it is ineligible to be the object to which a unique_ptr points (or any smart pointer, for that matter).
I have two structs:
struct A
{};
struct B
{
A& a;
};
and initialize them A a0; B b{a0};. But now, I want to move object a0 to new one:
A a1{std::move(a0)};
I don't know how to inform b that the value of it's member a should be changed. It's impossible to change value of standard reference, so I was thinking to change it into std::reference_wrapper or something similar. But still there is a problem - how to inform object b (specifically - its member which now is some kind of smart reference) that value of field a should be changed?
I was thinking about observer pattern, where A will be subject and B member referring to A will be observer. After moving subject, it will send all observers its new address. It may be solution, but maybe there is something easier?
As appendix, why I use something like that. In my project, I have another structure (simplified version below):
struct C
{
A a;
B b{a};
};
A is wrapper to memory (pointer to memory is given to A in constructor, but here not mentioned to keep it simple), it knows size of allocated memory and how to write and read it. B is object which knows how to insert some values to memory (i.e. how to insert int32_t - in LE or BE, how to serialize compound object). This two structures are in some closed library (company library, so I can open issue to change it, but it is used in some other projects, so I must be sure what kind of changes I need and are they really necessary). C gives me interface to put only specific objects into memory - I get only raw memory to construct it, so I must handle creating objects A and B inside it and outside C nobody needs to know what dependencies I use to write into this memory.
Structure C should be movable, because it will be returned using std::optional - its constructor is private and there exists static method create which builds object of C depending on status of some other operations needed to construct it (here described simply using bool argument):
static std::optional<C> create(bool v)
{
return v ? std::optional<C>{C()} : std::optional<C>{};
}
To tests, I also write constructor of C:
C()
{
std::cout << "C::C()" << std::endl << &a << std::endl << &b.a << std::endl;
}
and function which is trying to build this object:
auto c = C::create(true);
std::cout << "main" << std::endl;
std::cout << &(c.value().a) << std::endl;
std::cout << &(c.value().b.a) << std::endl;
Here is result of executing this test:
C::C()
0x7ffe8498e560
0x7ffe8498e560
main
0x7ffe8498e570
0x7ffe8498e560
which shows that C member b holds wrong reference now.
I'm open to criticism, because I know it could be bad design and maybe I should do it in some other way.
Your class C should be:
struct C
{
C(const C& c) : a(c.a), b(this->a) {}
C(C&& c) : a(std::move(c.a)), b(this->a) {}
A a;
B b{a};
};
Then your reference is still valid.
In certain embedded situations, memory needs to be moved with memcopy style functions (such as from external memory, or using closed API calls).
When such a C++ object needs to be moved this way, however it doesn't have a default constructor, you can't do something like this, :
class Object {
//local data
public:
Object(/* not a default constructor */){}
}
//elsewhere:
Object o; //compiler will complain here...
memcpy_like_function(src_address, &o, sizeof(o));
because Object doesn't have a default constructor, and thus the compiler will complain about creating Object o.
Some notes from things that have shown up in the comments:
memcpy_like_function is like memcpy, it isn't actually memcpy. The src_address isn't a pointer to an address I can reach, or an int representing a pointer to an address I can reach. It is an int representing an address in a memory space I can't reach. The only way for me to access this memory space is with this function.
Object doesn't have a default constructor, has no virtual functions, is neither inherited from, nor inherits anything. Object is trivially copyable.
What is the correct way to deal with creating such an object in this situation, without putting it on the heap? Preferably, I would like to get a stack allocated object that will behave correctly with scope and destructors. For the purposes of this question, Object is not inheriting from anything.
This seems like a horribly bad idea, but assuming that your memcpy_like_function actually works, then you can just add a constructor to Object
class Object {
//local data
public:
Object(void* src_address)
{
memcpy_like_function(src_address, this, sizeof(*this));
}
};
//elsewhere:
Object o(src_address);
because Object doesn't have a default constructor
When Object doesn't have a default constructor,
//Object o;//NG
there is no way to construct Object unless call another ctor or factory function. Because of that, you cannot call memcpy-like function.
When you have way to construct Object, to use memcpy-like function, Object class must grantee that it is trivially copyable class and standard-layout class(not equal to POD class).
trivial class : trivially copyable class && has no default user-defined constructor
POD class : trivial class && standard-layout class
You cannot safely copy an object using a memcpy-like function unless the object is a POD type. If the object is a POD type, you should be able to use:
char destination[sizeof(Object)];
memcpy_like_function(src_address, destination, sizeof(destination));
Object* ptr = reinterpret_cast<Object*>(destination);
My gut-feel says that that should work under all compilers for POD types. Whether it is cause for undefined behavior under some rules of the standard, I am not sure.
If that is cause for undefined behavior under some rules of the standard, you won't be able to save a POD-type to a binary file and read it from the binary file in a standards compliant manner. They rely on the bit-pattern written to a file and read from a file to represent the object.
The following program produces the expected result under g++.
#include <iostream>
#include <cstring>
struct Object
{
int i;
double d;
Object(int ii, double dd) : i(ii), d(dd) {}
};
int main()
{
Object o1(10, 20.34);
char dest[sizeof(Object)];
memcpy(dest, &o1, sizeof(dest));
Object* ptr = reinterpret_cast<Object*>(dest);
std::cout << o1.i << ", " << o1.d << std::endl;
std::cout << ptr->i << ", " << ptr->d << std::endl;
}
Update, in response to OP's comments
The following program works as expected under g++.
#include <iostream>
#include <cstring>
struct Object
{
int i;
double d;
Object(int ii, double dd) : i(ii), d(dd) {}
};
Object testFunction(Object o1)
{
char dest[sizeof(Object)];
memcpy(dest, &o1, sizeof(dest));
Object* ptr = reinterpret_cast<Object*>(dest);
return *ptr;
}
int main()
{
Object o1(10, 20.34);
Object o2 = testFunction(o1);
std::cout << o1.i << ", " << o1.d << std::endl;
std::cout << o2.i << ", " << o2.d << std::endl;
o2.i = 25;
o2.d = 39.65;
std::cout << o2.i << ", " << o2.d << std::endl;
}
What you could do is simply use an array of bytes, then cast.
Note: what you are doing is not really good C++ practice, typically you should use assignment operators or copy constructors, and you should stick to the safer C++ casts rather than a brute-force C-style cast.
Anyway, that being said, this code will work:
class Object {
int i;
public:
Object(int i) : i(i) {}
void foo() const {
std::cout << i << std::endl;
}
};
void bla() {
Object one(1);
char *bytes = new char[sizeof(Object)];
memcpy(bytes, &one, sizeof(Object));
Object &anotherOne = (Object &) *bytes;
anotherOne.foo();
const Object &oneMore = (Object) *bytes;
oneMore.foo();
Object *oneMoreTime = (Object *) bytes;
oneMoreTime->foo();
delete[] bytes;
}
The output is:
1
1
1
In summary, you need to allocate a region of memory on the stack or the heap that will become the Object instance.
I've just discovered std::shared_ptr's "aliasing constructor" and find myself asking "why doesn't std::unique_ptr have an corresponding one?
That is, if you want to allocate a Foo so that you can pass its Bar member to a function that should entirely manage the lifetime of the Foo, wouldn't it be nice to be able to do so?
#include <memory>
struct B {}
struct A {
B b;
}
void f(std::unique_ptr<B> b);
std::unique_ptr<A> a = std::make_unique<A>();
std::unique_ptr<B> b { std::move(a), &(a->b) }; // a now invalid.
f(std::move(b)); // f now responsible for deleting the A.
This works with std::shared_ptr ( http://ideone.com/pDK1bc )
#include <iostream>
#include <memory>
#include <string>
struct B {
std::string s;
};
struct A {
B b;
A(std::string s) : b{s} {};
~A() { std::cout << "A deleted." << std::endl; }
};
void f(std::shared_ptr<B> b) {
std::cout << "in f, b->s = " << b->s << " (use_count=" << b.use_count() << ")" << std::endl;
}
int main() {
std::shared_ptr<A> a = std::make_shared<A>("hello");
std::shared_ptr<B> b { a, &(a->b) };
a.reset(); // a now invalid.
std::cout << "before f, b->s = " << b->s << " (use_count=" << b.use_count() << ")" << std::endl;
f(std::move(b)); // f now responsible for deleting the A.
std::cout << "after f" << std::endl;
return 0;
}
outputs the expected
before f, b->s = hello (use_count=1)
in f, b->s = hello (use_count=1)
A deleted.
after f
Is there a logical reason why such a thing wasn't included? And/or, is it a bad idea to emulate it with a unique_ptr<B> with a custom deleter that deletes the A?
I believe the “problem” is that, unlike std::shared_ptr, std::unique_ptr's deleter is not type-erased. The default deleter of std::unique_ptr<T> (which has zero size, encoded into the type itself as a barely visible default type parameter) is simply [](T * p){ delete p; }. But it is clear that a std::unique_ptr<B> that was created via std::make_unique<B> and one that was made by pointing to a B member of an A object cannot have the same deleter. The deleter for the latter case would have to do some pointer arithmetic to obtain the original A * pointer back. Those two deleters could only have the same type if both would store an offset or an internal pointer to the original object. And that would no longer have zero size. std::unique_ptr was designed to have zero overhead compared to doing new and delete manually, which is a Good Thing. I don't see any immediate drawbacks from using your own deleters that store that additional pointer, though I'd still have to come across a use-case where I'd find this useful.
shared_ptr has reference counting overhead. In its reference counting block it also stores an explicit deleter (because if you are storing on the heap, what is a few more bytes?)
This is also why a shared_ptr to a base type can remember to delete derived types without a virtual dtor.
unique_ptr, on the other hand, stores its deleter in the instance, and the default deleter is stateless -- 0 bytes used. This makes unique_ptr zero overhead over a raw pointer in terms of memory usage.
A stateless deleter cannot remember to delete something else.
You can add a stateful deleter to unique_ptr that supports aliasing, but then you'll have to alias manually. One of the constructors takes both a pointer and a deleter.