Is it possible to make std::shared_ptr not to delete an object, but move it to a list?
#include <memory>
#include <list>
struct QObject { double socialDistance; };
int main()
{
std::list<QObject *> free_objects;
std::list<QObject *> used_objects;
QObject* p_object = new QObject{10};
free_objects.push_back(p_object);
{
std::shared_ptr<QObject> p(p_object);
}
//At this point p_object is moved from free_objects to used_objects,
//so I reuse the object later.
return 0;
}
If yes, is it possible to reuse not only the objects but also the control block to avoid memory reallocation?
Is it possible to make std::shared_ptr not to delete an object, but move it to a list?
You might use custom deleter for that:
std::shared_ptr<QObject> p(p_object, [&](QObject* p){
used_objects.push_back(p);
auto it = std::find(free_objects.begin(), free_objects.end(), p);
if (it != free_objects.end()) free_objects.erase(it);
});
Demo
Related
I'm writing a game framework, I have a vector<unique_ptr<Object>> list and I distribute pointers from that list by calling object.get() and sending that out. Before that I send out references instead of raw pointers but that resulted in other weird problem so I was told this is better. However when I remove a unique_ptr<Object>from the list, the raw pointers remains. I also can't deallocate them manually, I get an exception saying the pointer is not allocated.
So my question would be:
How do I delete raw pointers from removed unique_ptr's?
and is also a more general question:
Am I on the right track structure wise of passing pointers instead of references?
PxlObject* PxlFramework::AddObject(PxlObject* obj)
{
std::unique_ptr<PxlObject> u_ptr(obj);
objects_iterator = objects.insert(objects.end(), std::move(u_ptr));
return obj;
}
void PxlFramework::DeleteObject(PxlObject* obj) {
for(objects_iterator = objects.begin(); objects_iterator != objects.end(); ++objects_iterator)
{
if((*objects_iterator)->get_id() == obj->get_id())
{
//attempting to delete the raw pointer here but it will result in an error
delete obj;
//removing the unique_ptr<PxlObject> from the list here
std::swap((*objects_iterator), objects.back());
objects.pop_back();
break;
}
}
}
The whole point of std::unique_ptr is that it "owns" the object and it manages deletion automatically when the unique_ptr is destroyed. As such, you should not delete either a unique_ptr nor anything that a unique_ptr owns. To avoid this confusion, references are more common. Additionally, you have the oddity that your AddObject returns a pointer to a PxlObject that is not the one just added.
Something like this might be a little cleaner:
template<class Us...>
PxlObject& PxlFramework::AddObject(Us&&... obj)
{
std::unique_ptr<PxlObject> u_ptr(new PxlObject(std::forward<Us>(obj)...));
objects_iterator = objects.insert(objects.end(), std::move(u_ptr));
return **objects_iterator;
}
void PxlFramework::DeleteObject(PxlObject& obj) {
auto finder = [](std::unique_ptr<PxlObject>& p)->bool
{return obj.get_id()==p->get_id();};
auto it = find_if(objects.begin(), objects,end(), finder);
if (it != objects.end())
objects.erase(it);
else
throw ...;
}
You don't need delete the raw pointer directly you can use vector.erase instead. Here you have a simple example:
#include <iostream>
#include <algorithm>
#include <memory>
#include <vector>
using namespace std;
typedef vector<unique_ptr<int>> ptr_list_t;
void remove_number(int x, ptr_list_t& ptr_list)
{
for (ptr_list_t::iterator it = ptr_list.begin(); it != ptr_list.end(); ++it)
if (*(it->get()) == x) {
ptr_list.erase(it); // Use vector.erase for deleting objects from a vector.
// since it points to a unique_ptr, the object owned by it
// will be destroyed automatically.
break;
}
}
int main()
{
ptr_list_t ptr_list;
// Generating the pointer to numbers. 0 - 9
for (int i = 0; i < 10; i++)
ptr_list.push_back(unique_ptr<int>(new int(i)));
// Remove the number 3.
remove_number(3, ptr_list);
// Printing the list. The number 3 will not appear.
for (ptr_list_t::iterator it = ptr_list.begin(); it != ptr_list.end(); ++it)
cout << *(it->get()) << endl;
return 0;
}
Other thing, I'm agreed with #MooingDuck: you should not delete either a unique_ptr nor anything that a unique_ptr owns. But you sure can. Take a look on unique_ptr.release. This function frees the ownership of the managed object.
Code as follows:
#include "MyObject.h"
#include <vector>
#include <memory>
class MyCollection {
private:
std::vector<std::unique_ptr<MyObject*>> collection;
public:
MyCollection();
virtual ~MyCollection();
int insert(MyObject* newValue);
};
int MyCollection::insert(MyObject* newValue) {
if (collection.empty()) {
collection.push_back(move(make_unique<MyObject*>(newValue)));
return 0;
}
int index = collection.size()-1;
collection.resize(collection.size()+1);
vector<unique_ptr<MyObject*>>::reverse_iterator pos = collection.rbegin();
for ( ; (index >= 0) && (pos+1) != collection.rend() && stringToUpper((*(pos+1)->get())->getObjectName()) > stringToUpper(newValue->getObjectName()); ++pos) {
pos = (pos+1);
index--;
}
pos = ?newValue; // How do I do this?
//pos->reset(move(make_unique<MyObject*>(newValue)));
return index+1;
}
make_unique() implementation taken from http://scrupulousabstractions.tumblr.com/post/37576903218/cpp11style-no-new-delete
My question is there a way to do what I'm attempting with the assignment to the reverse_iterator (pos = newValue)? One of my pitiful attempts is shown in the commented code.
Thanks!
Firstly, as others have pointed out, you want a vector<unique_ptr<MyObject>> not vector<unique_ptr<MyObject*>>. It is fine to have a unique_ptr containing an abstract class (make sure the base class has a virtual destructor). You can implicitly cast from a unique_ptr containing a derived class.
Ideally I think MyCollection::insert should take a unique_ptr not a raw pointer so that the calling code creates objects using make_unique in the first place but let's leave it like it is for now.
I think you have a bit of confusion with make_unique. make_unique is designed to create an object and wrap it in a unique_ptr in one go, safely. Your MyCollection::insert doesn't really need to use make_unique because the object is already created. unique_ptr has a constructor that takes a raw pointer so you can create one directly from the raw pointer.
You can then push the unique_ptr onto the collection or replace unique_ptrs in the collection with new unique_ptrs fine:
class MyObject {
public:
virtual ~MyObject() = 0
};
MyObject::~MyObject() {}
class SimSolverObject : public MyObject {
};
class MyCollection {
private:
std::vector<std::unique_ptr<MyObject>> collection;
public:
void insert(MyObject* newValue);
};
void MyCollection::insert(MyObject* newValue) {
//...
// if we want to add to the collection
collection.push_back(std::unique_ptr<MyObject>(newValue));
// if we want to replace at iterator pos in collection
*pos = std::unique_ptr<MyObject>(newValue);
}
// calling code
MyCollection mc;
MyObject* newValue = new SimSolverObject();
mc.insert(newValue)
If you do decide to change MyCollection::insert to take a unique_ptr it would look something like this:
void MyCollection::insert(std::unique_ptr<MyObject> newValue) {
//...
// if we want to add to the collection
collection.push_back(std::move(newValue));
// if we want to replace at pos
*pos = std::move(newValue);
}
Edit: Your for loop looks a bit suspicious. I am not quite sure what you are trying to do but are you sure you want to increment the iterator twice? Once in the body of the for and once in the loop expression? I suspect the iterator is skipping over your condition and going out of bounds of the vector. When it hits the index condition you may be left with an invalid iterator.
I have some legacy-era code at work that takes in a double-pointer and allocates memory to it. A shortened example of it would look something like this:
struct LegacyObj
{
int a;
double b;
};
void LegacyAllocator(LegacyObj** ppObj)
{
*ppObj = (LegacyObj*)malloc(sizeof(LegacyObj));
}
void LegacyDeleter(LegacyObj** ppObj)
{
free(*ppObj);
}
The actual LegacyAllocator function is ~100 lines and mixes reading from files with creating a linked list of LegacyObj pointers, and isn't something I'd be able to get away with rewriting right now. I would like, however, to make the use of this function a bit safer, avoiding any memory leaks that may occur from exceptions &tc. The first solution I came up with was to wrap it up in a class and handle calling the legacy functions in the ctor/dtor.
class RAIIWrapper
{
public:
RAIIWrapper()
:obj{nullptr}
{
::LegacyAllocator(&obj);
}
RAIIWrapper(RAIIWrapper&& that)
: obj{ that.obj}
{
that.obj = nullptr;
}
RAIIWrapper& operator=(RAIIWrapper&& that)
{
RAIIWrapper copy{std::move(that)};
std::swap(obj, copy.obj);
return *this;
}
~RAIIWrapper ()
{
::LegacyDeleter(&obj);
}
private:
LegacyObj* obj;
};
But I'm curious - is there a way to do this using std::shared_ptr or std::unique_ptr? I've not been able to come up with a solution without having to keep the original pointer passed to LegacyAllocator around.
Yes, you can use a custom deleter with std::unique_ptr or std::shared_ptr, for example:
struct Deleter {
void operator()(LegacyObj *p) const {
LegacyDeleter(&p);
}
};
std::unique_ptr<LegacyObj, Deleter> MakeLegacyObj() {
LegacyObj *p = 0;
LegacyAllocator(&p);
return std::unique_ptr<LegacyObj, Deleter>(p);
}
std::unique_ptr<LegacyObj, Deleter> p = MakeLegacyObj();
And, as correctly pointed out by #Dave, this works with shared_ptr too:
std::shared_ptr<LegacyObj> p = MakeLegacyObj();
You can use unique_ptr to delete the memory, but you'll have to provide a custom Deleter class since the memory is allocated using malloc rather than new.
Better yet, change the allocation code to use new instead and just use unique_ptr. If you go down this road you can just have the allocator return a unique_ptr instead of a pointer to the memory.
Assuming you need to provide your own custom deleter, here is one way you might do it:
template <typename T>
class MallocDeleter
{
public:
void operator() (T* obj) const
{
LegacyDeleter (*obj);
}
};
typedef std::unique_ptr <LegacyObj, MallocDeleter <LegacyObj>> unique_legacy_ptr;
You could also probably provide a make_unique_legacy type function which allocates by calling LegacyAllocator, instead of having to initialize the unique_ptr yourself.
You can create a factory function for unique_ptrs like this:
typedef void(* LegacyDeleterType)(LegacyObj*);
typedef std::unique_ptr<LegacyObj,LegacyDeleterType> UniqueLegacyPtr;
UniqueLegacyPtr makeUniqueLegacyObj()
{
LegacyObj * p = nullptr;
LegacyAllocator( &p );
return UniqueLegacyPtr( p, [](LegacyObj*p){ LegacyDeleter(&p); } );
}
You can now use that to create unique_ptrs and you can also assign to shared_ptrs which capture the custom deleter automatically at construction:
int main()
{
auto unique = makeUniqueLegacyObj();
std::shared_ptr<LegacyObj> shared = makeUniqueLegacyObj();
}
In a C++ application I create in various points of a class User objects of a particular class Vehicle with operator new and right after each instantiation I put them in a vector which is a property of the first class (i.e. vector< Vehicle* > v1).
At the running time of the program it is likely I need to some point to delete the objects of class Vehicle. Should I use definitely the operator delete? If so, how is this be done to a vector? Is there any predefined function of the vector to do the same work? When I delete them, what happens to the properties of Vehicle class, which in this occasion are pointers to other Objects? Are they also deleted?
If it is no need to delete them until the end of the program, are they deleted by the destructor or should delete them "manually"?
Here's the C++ way:
#include <memory>
#include <vector>
#include "vehicle.hpp"
typedef std::vector<std::unique_ptr<Vehicle>> vehicle_container;
#include "derivedvehicles.hpp"
int main()
{
vehicle_container v;
//...
v.emplace_back(new Car);
v.emplace_back(new Bike);
//...
} // baam, everything is cleaned up
Here a better way (IMHO):
#include <boost/ptr_container/ptr_vector.hpp>
#include "vehicle.hpp"
typedef boost::ptr_vector<Vehicle> vehicle_container;
#include "derivedvehicles.hpp"
int main()
{
vehicle_container v;
//...
v.emplace_back(new Car);
v.emplace_back(new Bike);
//...
v[0].go(); // Elements accessed as if they are objects (not pointers)
// Which makes using in any of the standard
// algorithms trivial.
} // ta-da, everything is cleaned up
I'm trying to delete everything from a std::vector by using the following code
vector.erase( vector.begin(), vector.end() );
but it doesn't work.
Update: Doesn't clear destruct the elements held by the vector? I don't want that, as I'm still using the objects, I just want to empty the container
I think you should use std::vector::clear:
vec.clear();
EDIT:
Doesn't clear destruct the elements
held by the vector?
Yes it does. It calls the destructor of every element in the vector before returning the memory. That depends on what "elements" you are storing in the vector. In the following example, I am storing the objects them selves inside the vector:
class myclass
{
public:
~myclass()
{
}
...
};
std::vector<myclass> myvector;
...
myvector.clear(); // calling clear will do the following:
// 1) invoke the deconstrutor for every myclass
// 2) size == 0 (the vector contained the actual objects).
If you want to share objects between different containers for example, you could store pointers to them. In this case, when clear is called, only pointers memory is released, the actual objects are not touched:
std::vector<myclass*> myvector;
...
myvector.clear(); // calling clear will do:
// 1) ---------------
// 2) size == 0 (the vector contained "pointers" not the actual objects).
For the question in the comment, I think getVector() is defined like this:
std::vector<myclass> getVector();
Maybe you want to return a reference:
// vector.getVector().clear() clears m_vector in this case
std::vector<myclass>& getVector();
vector.clear() should work for you. In case you want to shrink the capacity of the vector along with clear then
std::vector<T>(v).swap(v);
vector.clear() is effectively the same as vector.erase( vector.begin(), vector.end() ).
If your problem is about calling delete for each pointer contained in your vector, try this:
#include <algorithm>
template< typename T >
struct delete_pointer_element
{
void operator()( T element ) const
{
delete element;
}
};
// ...
std::for_each( vector.begin(), vector.end(), delete_pointer_element<int*>() );
Edit: Code rendered obsolete by C++11 range-for.
Use v.clear() to empty the vector.
If your vector contains pointers, clear calls the destructor for the object but does not delete the memory referenced by the pointer.
vector<SomeClass*> v(0);
v.push_back( new SomeClass("one") );
v.clear(); //Memory leak where "one" instance of SomeClass is lost
Is v.clear() not working for some reason?
If you keep pointers in container and don't want to bother with manually destroying of them, then use boost shared_ptr. Here is sample for std::vector, but you can use it for any other STL container (set, map, queue, ...)
#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>
struct foo
{
foo( const int i_x ) : d_x( i_x )
{
std::cout << "foo::foo " << d_x << std::endl;
}
~foo()
{
std::cout << "foo::~foo " << d_x << std::endl;
}
int d_x;
};
typedef boost::shared_ptr< foo > smart_foo_t;
int main()
{
std::vector< smart_foo_t > foos;
for ( int i = 0; i < 10; ++i )
{
smart_foo_t f( new foo( i ) );
foos.push_back( f );
}
foos.clear();
return 0;
}
Adding to the above mentioned benefits of swap(). That clear() does not guarantee deallocation of memory. You can use swap() as follows:
std::vector<T>().swap(myvector);
If your vector look like this std::vector<MyClass*> vecType_pt you have to explicitly release memory ,Or if your vector look like : std::vector<MyClass> vecType_obj , constructor will be called by vector.Please execute example given below , and understand the difference :
class MyClass
{
public:
MyClass()
{
cout<<"MyClass"<<endl;
}
~MyClass()
{
cout<<"~MyClass"<<endl;
}
};
int main()
{
typedef std::vector<MyClass*> vecType_ptr;
typedef std::vector<MyClass> vecType_obj;
vecType_ptr myVec_ptr;
vecType_obj myVec_obj;
MyClass obj;
for(int i=0;i<5;i++)
{
MyClass *ptr=new MyClass();
myVec_ptr.push_back(ptr);
myVec_obj.push_back(obj);
}
cout<<"\n\n---------------------If pointer stored---------------------"<<endl;
myVec_ptr.erase (myVec_ptr.begin(),myVec_ptr.end());
cout<<"\n\n---------------------If object stored---------------------"<<endl;
myVec_obj.erase (myVec_obj.begin(),myVec_obj.end());
return 0;
}
class Class;
std::vector<Class*> vec = some_data;
for (unsigned int i=vec.size(); i>0;) {
--i;
delete vec[i];
vec.pop_back();
}
// Free memory, efficient for large sized vector
vec.shrink_to_fit();
Performance: theta(n)
If pure objects (not recommended for large data types, then just
vec.clear();