How to implement a class containing a unique_ptr to a vector containing elements of the same class to build kind of a hierarchical structure.
I had implemented the same example using normal pointer before. That had worked fine.
Now I am trying to adapt to c++11 using unique_ptr.
The line marked with the "works" comment seems to do what I expect. The same kind of statement used inside the copy construct seems to fail copying the actual contents of the vector into the new instance.
I am using Visual Studio 2013.
Background information:
I have stripped down the class. The iPOD is only representing a lot of other members also existing within the class.
The class is used as representation of single attributes of an HMI widget tree.
#include <string>
#include <memory>
#include <vector>
void func();
struct Attribute;
typedef std::vector<Attribute> AttributeVector;
struct Attribute
{
int m_iPOD;
//AttributeVector *m_kAttrVectorPtr; // Old version before unique_ptr did work...
std::unique_ptr<AttributeVector> m_kAttrVectorPtr; // New version using unique_ptr
/** Default constructor. */
Attribute()
: m_iPOD(-1), m_kAttrVectorPtr(nullptr)
{
}
/** Constructs an attribute. */
Attribute(int iPOD, std::unique_ptr<AttributeVector> kAttrs)
: m_iPOD(iPOD), m_kAttrVectorPtr(std::move(kAttrs))
{}
/** Copy constructor.
#param kSource Source instance.
*/
Attribute(const Attribute& kSource)
: m_iPOD(kSource.m_iPOD)
{
if(kSource.m_kAttrVectorPtr)
m_kAttrVectorPtr = std::unique_ptr<AttributeVector> (new AttributeVector(kSource.m_kAttrVectorPtr));
else
m_kAttrVectorPtr = nullptr;
}
/** Assignment operator.
#param kSource Source instance.
*/
Attribute& operator=(const Attribute& kSource)
{
m_iPOD = kSource.m_iPOD;
if(kSource.m_kAttrVectorPtr)
m_kAttrVectorPtr = std::unique_ptr<AttributeVector> (new AttributeVector(kSource.m_kAttrVectorPtr));
else
m_kAttrVectorPtr = nullptr;
return *this;
}
bool operator==(const Attribute& rkOther) const
{
return m_iPOD == rkOther.m_iPOD;
// Todo real compare m_kAttrVectorPtr == rkOther.m_kAttrVectorPtr;
}
bool operator!=(const Attribute& rkOther) const
{
return !operator==(rkOther);
}
};
int main()
{
AttributeVector kVector;
Attribute kAttr1;
kAttr1.m_iPOD = 101;
kAttr1.m_kAttrVectorPtr = nullptr;
Attribute kAttr2;
kAttr2.m_iPOD=102;
kAttr2.m_kAttrVectorPtr = nullptr;
kVector.push_back(kAttr1);
kVector.push_back(kAttr2);
Attribute kAttr;
kAttr.m_iPOD=100;
kAttr.m_kAttrVectorPtr = std::unique_ptr<AttributeVector> (new AttributeVector(kVector)); // Works result= kattr with a vector of 2 attributes
Attribute kAttrCopy(kAttr);// does not work. Only one entry within m_kAttrVectorPtr after copy instead of the 2 from above
Attribute kAttrAssign;
kAttrAssign = kAttr;// does not work. Only one entry within m_kAttrVectorPtr after copy instead of the 2 from above
return 0;
}
The idea of unique_ptr is to manage an object on the heap, i.e. to automatically delete it once the pointer goes out of scope. A vector does the same with its contents, so allocating a vector on the heap is just a waste of resources (time and memory). This means that already your previous design which allocated a vector was flawed. Instead of
struct A {
vector<A> *pvec;
A() : pvec(new vector<A>) {}
~A() { delete pvec; }
};
(which you are attempting to improve with unique_ptr), simply use
struct A {
vector<A> vec; // requires 24 bytes on 64bit machines
// auto generated constructor and destructor
};
Alternatively, if the number of objects hold in the vector are known at the start, you could use a unique_ptr:
struct A {
unique_ptr<A[]> vec; // requires 8 bytes on 64bit machines
A(size_t n)
: vec(new A[n]) {}
};
Replace new AttributeVector(kSource.m_kAttrVectorPtr) with new AttributeVector(*kSource.m_kAttrVectorPtr).
You can't construct a vector with any pointer, but with the value itself. (*)
Related
I'm new to C++ and struggling to setup a proper class with private members and accessing them. Basically, I have a vector of Layers which make up a Stack. I'd like to create a simple function which simply adds a layer to the stack. I've tried to simplify this example to explain my problem.
// Stack.h
namespace NS {
class Stack
{
public:
Stack() {
}
virtual ~Stack() {
}
std::vector<Layer> const &getLayers() const;
virtual Layer* AddLayer(TextureBase texture);
protected:
std::vector<Layer> _layers;
}
This is my cpp file
//Stack.cpp
namespace NS {
std::vector<Layer> const &Stack::getLayers() const {
return _layers;
}
Layer* Stack::AddLayer(TextureBase texture) {
Layer* newLayer = new Layer();
newLayer->setTexture(texture);
std::vector<Layer> layerStack = Stack::getLayers();
layerStack.push_back(*newLayer);
return newLayer;
}
}
In my main file I create the stack and then try to add the layer like this:
auto myStack = getStack();
myStack->AddLayer(myTexture);
However, when I place a breakpoint after this line, myStack doesn't contain any layers (the size is 0). I can step through the AddLayer function and it does appear to add the Layer to the Stack... but perhaps it's not referencing the vector correctly. Can anyone provide some guidance as to why this is occurring?
The problem is that layerStack is a local copy of _layers:
std::vector<Layer> layerStack = Stack::getLayers();
You are pushing your new layer to this local copy, not to your data member. You need to take a reference to your data member instead:
std::vector<Layer>& layerStack = Stack::getLayers();
Alas, this won't compile because your getLayers function returns a const reference. You need to add a non-const counterpart:
std::vector<Layer>& getLayers();
There are 3 issues you have to address.
1. Layer* newLayer = new Layer() will dynamically allocate a Layer object, but when you insert it into your vector, you dereference it, so you end up doing a push_back on a COPY of your Layer object. You could have just used Layer newLayer = Layer(), or if I understand your intent correctly a vector of Layer pointers:
vector<Layer*> _layers;
In order to be editable, your getLayers() function must NOT return a const-ref to the _layers vector. Change it to std::vector& getLayers();
Finally, std::vector layerStack = Stack::getLayers(); creates a COPY of the vector returned by getLayers(). Change it to std::vector& layerStack = Stack::getLayers()
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();
}
I have a vector of pointers..
I want to access my Image object via the pointer stored in the std::vector. After the iterator finds my object, I want to access/modify certain members of the Image object being pointed by the pointer in a vector.
Please help !!
class Image;
typedef std::vector <Image*> VecImages;
typedef VecImages::iterator ImagesIter;
class My_Images
{
private:
VecImages m_Images;
public:
My_Images() {}
~My_Images();
void addImage(Image* img) {m_Images.push_back(img);}
Image* getImage(string& rID);
};
// get image method
Image* My_Images::getImage(string& rID)
{
ImagesIter foundImg = m_Images.begin();
while (foundImg != m_Images.end()) {
Image* img = *foundImg; ***<<<<----------- [EXC_BAD_ACCESS is reported here]***
const string &strID = img->get_strRId();
if (strID == rID) {
return (*foundImg);
}
++foundImg;
}
return NULL;
}
// get-id method
const string& Image::get_strRId(void) {return m_strRId;}
// copy constructor
Image::Image(const Image& _src)
: m_strRId(_src.m_strRId)
{}
I have another class named builder ... and there I'm invoking the addImage() method like this ...
My_Images *m_images; (I create this 'new')
Image *currImg = new Image;
currImg->setName("ABC"); ... and several other 'set' functions are called on currImg (mainly setting std::string's).
And then I add it into the vector using following call.
m_images->addImage(currImg);
And once this is stored in the vector, I giveup it's ownership.
currImg = NULL;
template <class _Type>
_Type* ChAutoPtr<_Type>::giveUpOwnership()
{
#ifdef VERIFY_WITH_SHADOW_STACK
// Check to see if the last auto-ptr is the one that is being
// given up!!!
if (m_pInstance != NULL)
{
void* pTop = g_cleanupStackShadow.top();
ChASSERT(pTop == m_pInstance);
g_cleanupStackShadow.pop();
}
#endif /* _DEBUG */
_Type* pTmp = m_pInstance;
m_pInstance = NULL;
return pTmp;
}
EXC_BAD_ACCESS means that you either (1) added uninitialized object to the vector or (2) its life was short and it ceased to exist prior the invocation of getImage(). You may try to enable NSZombies or verify the life span and scope of images added to the vector. Preferably, try using smart pointers (e.g. shared_ptr).
I maintain a old C++ application which has a lot of classes like below:
class ClassWithALotOfVectors
{
std::vector<int> _vector1;
std::vector<int> _vector2;
// a lot of other vector datamembers go here
std::vector<double> _vectorN;
};
that is - a data type where members a vectors of double or int.
The thing is - these vectors are never populated at the same time - and therefore
when we create 100000 instances of ClassWithALotOfVectors - memory usage adds up
to impressive number even though only 10% of these vector are in use.
So I decided to write a small "allocate on demand" vector class.
It is a wrapper around std::vector - where internal vector only create when
accessed for the first time (using two getters - ref() & const_ref() methods)
When I replaced std::vector in a ClassWithALotOfVectors class with a compact_vector
as below:
class ClassWithALotOfVectors2
{
compact_vector<int> _vector1;
compact_vector<int> _vector2;
compact_vector<double> _vectorN;
};
did a few tests and the results were promising - memory usage went down
dramatically, but suddenly found that application does not release memory
at the end - the memory consumptions grows in much slower rate than it
is used to be - but application does not seem to be deallocating the memory
at the end.
Can you look at my implementation of the compact_vector
and see if you can spot something wrong with a memory management.
template <class T> class compact_vector
{
public:
compact_vector<T>()
:_data(NULL)
{
}
~compact_vector<T>()
{
clear();
}
compact_vector<T>(const compact_vector<T> & rhs)
:_data(NULL)
{
if (NULL != rhs._data)
{
_data = new std::vector<T>(*(rhs._data));
}
else
{
clear();
}
}
// assignment
//
compact_vector<T> & operator=(const compact_vector<T> & rhs)
{
if (this != &rhs)
{
clear();
if (NULL != rhs._data)
{
_data = new std::vector<T>(*rhs._data);
}
}
return *this;
}
compact_vector<T> & operator=(const std::vector<T> & rhs)
{
clear();
if (!rhs.empty())
{
_data = new std::vector<T>(rhs);
}
return *this;
}
const std::vector<T> & const_ref()
{
createInternal();
return *_data;
}
std::vector<T> & ref()
{
createInternal();
return *_data;
}
void clear()
{
if (NULL != _data)
{
_data->clear();
delete _data;
_data = NULL;
}
}
private:
void createInternal()
{
if (NULL == _data)
{
_data = new std::vector<T>();
}
}
private:
compact_vector<T>(const std::vector<T> & rhs)
{
}
compact_vector<T>(std::vector<T> & rhs)
{
}
compact_vector<T>(std::vector<T> rhs)
{
}
std::vector<T> * _data;
};
Most implementations of std::vector don't acquire memory until you need it, and the size of a vector is usually just a few (3 + possibly extra debug information) pointers. That is, std::vector<int>() will not allocate space for any object. I believe that you are barking at the wrong tree here.
Why is your original code having a much higher memory usage? Are you expecting clear() to release memory?
If so you are mistaken. The clear() function destroys the contained elements but does not release the allocated memory. Consider adding a wrapper to clear the memory that uses the following idiom:
std::vector<int>().swap( _data );
What the previous line does is creating a new empty vector (usually no memory attached, not mandated, but common implementation) and swapping the contents of the two vectors. At the end of the expression, the temporary contains the data that was originally held by the _data vector and _data is empty and with no memory (implementation defined). The temporary is destructed at the end of the full expression and memory is released.
Alternatively, if your compiler supports C++11, you can use shrink_to_fit() after calling clear(). The standard does not require shrink_to_fit() to actually shrink to fit, but for an empty vector I would expect the implementation to do it. Test it by calling capacity() after the call and seeing whether it has gone down to 0 or not.
Use smart pointers. In this case, std::unique_ptr with hand-made copy ctor / assignment operator. Suddenly, all your problems are solved. It's like magic!
Sorry to sound so snarky, but memory leaks always have the same answer: smart pointer.
Since you are saying it's an old application and can't do much with it. How about having all the vectors having zero size reserved during construction.
It's tedious, but since old app and not much is expected to be modified. May be worth the effort for once.
class ClassWithALotOfVectors
{
std::vector<int> _vector1;
std::vector<int> _vector2;
// a lot of other vector datamembers go here
std::vector<double> _vectorN;
public:
ClassWithALotOfVectors() : Init() { }
void Init()
{
vector1.reserve(0);
/// Like wise other vectors.
}
};
The idea that only one vector at a time is ever in use sounds like the union concept. Sure enough, C++ has an element called an anonymous union that does precisely this.
class ClassWithALotOfVectors {
public:
union {
std::vector<double> _vector1;
std::vector<double> _vector2;
};
ClassWithALotOfVectors() { new(&_vector1) std::vector<double>; };
~ClassWithALotOfVectors() { _vector1.~vector<double>(); };
};
Because the union can't know which element's constructor to call, default constructors are disabled in the union and you have to manually construct and deconstruct the union's element, but I think this will accomplish what you are looking for, aliasing _vector1 and _vector2 to the same element but placing both in the ClassWithALotOfVectors namespace, then deallocating the vector when the destructor for ClassWithALotOfVectors is called.
For more on anonymous unions see:
CPP reference: Anonymous Unions