C++ Copy data pointed at by pointer - c++

I'm a long-time reader, and first-time poster... I've searched long and hard to find an answer to something that's really boggling my mind right now. I must be missing something, as I believe this should work...
I'm trying to create a datatable class that will contain it's own copies of the objects passed to it. I've decided to use std::map's to contain this data. See the example code below:
typedef std::map <std::string, myVar *> myVarContainer;
class myObj
{
public:
myObj(void);
virtual ~myObj(void);
void setVar(std::string Key, myVar & Var);
myVar * getVar(std::string Key);
void release()
{
for (myVarContainer::iterator i = VarContainer->begin(); i != VarContainer->end(); ++i)
{
delete (i->second);
}
VarContainer->clear();
};
myVarContainer * VarContainer;
};
typedef std::map <myVar, myObj *> myRow;
class myTable
{
public:
myTable(void);
virtual ~myTable(void);
void addDataPoint(myVar RowID, myVar ColID, myObj * Data)
{
std::map <myVar, myRow *>::iterator i = m_Rows->find(RowID);
if (i == m_Rows->end())
{
m_Rows->insert(make_pair(RowID, new myRow()));
}
i = m_Rows->find(RowID);
// i thought the below line would be creating a copy of the data?
// I thought this logic went:
// 1. create a new object copied from the value of 'Data'
// 2. return a pointer to this object and pair with the 'colID'
// 3. make this into a pair and insert into the main map
i->second->insert(make_pair(ColID, new myObj(*Data)));
};
protected:
std::map <myVar, myRow *> * m_Rows;
}
int main()
{
myVar a, b, c, d;
myObj * o = new myObj();
o->setVar("test", a);
o->setVar("test2", b);
myTable * tab = new myTable();
myVar x1, y1, x2;
tab->addDataPoint(y1, x1, o);
o->release(); // this clears out both 'o' and the values in 'tab'!?!?
//at this point tab has no data in its object at y1,x1???
o->setVar("test3", c);
o->setVar("test4", d);
tab->addDataPoint(y1, x2, o);
}
What I'm noticing is that my data is deleted too early. I believe I've missed something... I had thought I was creating a copy of the data referenced by the pointer and then storing a newly instance'd pointer in my map... Any thoughts? I appreciate any help!

So one of the problems that using (owning) raw pointers in containers is that you need to manually delete the instances yourself. I presume that myObj::~myObj does just that (iterates the container deleting all elements before deleting the container itself).
The line:
i->second->insert(make_pair(ColID, new myObj(*Data)));
Is copy constructing a myObj from Data.
Unfortunately, because you are not defining a copy constructor for myObj the compiler will generate one for you which will just copy the pointer to the VarContainer member. It won't create a new copy of the map or anything that it refers to internally. Once a copy is created you then have two instances which both point to the same container and both instances think that they own it. When the first one gets destructed it will seem to ok but actually leaves the other instance pointing to freed memory. As soon as the longest lived instance tries to do anything using this container pointer something bad will happen.
You could fix this by storing the maps by value and not by pointer:
typedef std::map<std::string, myVar> myVarContainer;
typedef std::map<myVar, myObj> myRow;
Also change myObj::VarContainer to be a non-allocated member. This means that everything now gets copied correctly and a copy will not reference anything from the original.
Note that you can also use smart pointers (such as std::shared_ptr) instead of raw pointers but you will still need to be careful with that as, although copying would be safe, they share data with the original which might not be what you expect.
You should take a look at the following:
http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)

It seems that you are indeed creating a copy of the object, but then when you release(), you are releasing the VarContainer (deleting all items and using clear()), so the copy you created before (with a copy of the pointer, not the actual container) is left with a pointer to an empty container.

Related

Delete a key/value pair from an unordered_map C++

I have two questions. First I have an std::unordered_map<int,Object*> where Object looks like this:
// Object.hpp
class Object {
public:
Object();
~Object(){};
int Id();
void setId(int i);
private:
int id;
};
This unordered_map is put in a class called DataSet which is used as a container to hold all of these objects. In DataSet I want to be able to properly erase a key/value pair given an integer id from Object.
To do this I tried to create an iterator that finds the key by id, and then deletes the pointer to the iterator, and finally erases the key/value pair. It looks like this:
int DataSet::deleteObject(int id)
{
std::unordered_map<int,Object*>::iterator found_it = objects.find(id);
if(found_it != objects.end()) {
delete *found_it;
objects.erase(found_it);
}
return 1;
}
However, when I compile it I get this error:
dataset.cpp:44:9: error: cannot delete expression of type 'value_type'
(aka 'pair<key_type, mapped_type>')
delete *found_it;
Is there a correct way to erase the Object at that location?
Second, when writing a clear() method, I realize I can't just do objects.clear(), so I was wondering if there was a way to actually clear the unordered_map.
For example when I used a std::vector I was able to do this:
std::vector<Object *>::reverse_iterator object;
for(object = objects.rbegin(); object < objects.rend(); object++)
delete(*object);
objects.clear();
But that won't work now for an unordered_map, so what is the right way to do this? The requirements that I am under make it so I can't change Object, or how the unordered_map is set up.
The first part of the question is quite simple: the iterator reference the value and a given location which is of type std::pair<int const, Object*>. You can't delete such an object. You'll get the pointer using the second part of the object, e.g.:
delete found_it->second;
However, it is actually quite uncommon and error-prone to take care of this kind of maintenance. You are much better off not storing an Object* but rather a std::unique_ptr<Object>: the std::unique_ptr<Object> will automatically release the object upon destruction. That would also simplify removing an element:
int DataSet::deletObject(int id) {
return objects.erase(id);
}
When storing std::unique_ptr<Object> you'll probably need to be a bit more verbose when inserting elements into the container, though, e.g.:
objects.insert(std::make_pair(key, std::unique_ptr<Object>(ptr)));
Given that your Object class doesn't have any virtual functions you could probably just store an Object directly, though:
std::unordered_map<int, Object> objects;
Dereferencing an unordered_map::iterator produces a unordered_map::value_type&, i.e. pair<const Key, Value>&.
You want
delete found_it->second;
objects.erase(found_it);
Stepping back a bit, are you sure you even need std::unordered_map<int,Object*> instead of std::unordered_map<int,Object>? If you use the latter you wouldn't have to worry about deleteing anything.
If you do need the mapped_type be an Object* you should use std::unordered_map<int,std::unique_ptr<Object>>. Again, this makes it unnecessary to manually delete the values before erasing an entry from the map.
Also, if Object is the base class for whatever types you're going to be adding to the map, don't forget that it needs a virtual destructor.

Copying local object to vector

I have a singleton class that holds a vector with data. My problem is that I want to add data to that vector using the standard push_back() method. I have an object I want to save in that vector but it's a local object (created in a method in another class). Obviously, at the end of this method the local variable would be deleted which is fine because I do a push_back() of that data to the vector.
Now, this works fine for as long the method didn't end. After it ends the data is gone. This seems weird because push_back() should be using the copy constructor right? Now, I tried to add the local variable to the vector by reference, by value, as a pointer and with the move constructor in C++11 but all those things don't seem to work.
So this is the setup of the problem class-wise:
ConnectionManager (holds the vector)
ClassA (which has a method with the local typeX object)
So, in short, I want the object created in a method in ClassA to be available after that method but in the vector from the ConnectionManager class.
EDIT: Here's the typeX I'm talking about:
struct Connection
{
SOCKET socket;
PlayerData* pPlayerData;
};
socket is just your normal winsock SOCKET variable and PlayerData* is a custom object created with Google's ProtocolBuffer.
FURTHER EDIT:
This is how I create the data:
Predefined::Connection connTemp;
connTemp.socket = 0;
connTemp.pPlayerData = data;
MultiplayerData::GetInstance()->AddPlayerToGame(connTemp);
connTemp.pPlayerData is, to answer the question, a locally created variable but it's created as a pointer.
If the issue is that you're creating the PlayerData data, and expecting to have the same data be still available after the function exits, it looks like you should use a type such as std::shared_ptr<PlayerData> instead of a naked PlayerData*.
#include <vector>
#include <memory>
//..
class PlayerData
{
int x, y, z;
public:
PlayerData(int a1, int a2, int a3) : x(a1), y(a2), z(a3) {}
};
typedef std::shared_ptr<PlayerData> PlayerDataPtr;
struct Connection
{
SOCKET socket;
PlayerDataPtr pPlayerData;
};
typedef std::vector<Connection> ConnectionVector;
void foo()
{
auto data = std::make_shared<PlayerData>(1, 2, 3); // create data dynamically
//...
Connection connTemp;
connTemp.socket = 0;
connTemp.pPlayerData = data;
MultiplayerData::GetInstance()->AddPlayerToGame(connTemp); // this does the push_back
//...
}
Since pPlayerData is now a shared_ptr, those copies that vector will generate just bump up a reference count, and conversely, when those copies are destroyed, the reference count is decremented. When the reference count reaches 0, then the data will indeed be deleted.
If you haven't called reset on the shared pointer, this is more or less, your guarantee that the data you created before the push_back was done will exist, as long as that entry in the vector wasn't removed.
Also, I edited the example to show a simpler PlayerData class. Note how make_shared is used.
Struct Connection will indeed be copied into the vector, so you don't need to worry about end of its lifetime. pPlayerData however needs to point into a memory allocated with new and owned by somebody.
connTemp.pPlayerData = data;
Where does the 'data' come from - generated locally?
The 'data' needs to be allocated by 'new' at some point.
If the "data" are all unique, consider using auto_ptr or unique_ptr for the type of pPlayerData member - C++ doesn't automatically manage standard pointers. If they're not unique, use shared_ptr or intrusive_ptr - can start with shared and retrofit intrusive later.

Correct use of std::map as class member

In the past I always created a map like this:
class TestClass
{
private:
std::map<int,int> *mapA;
};
TestClass::TestClass
{
mapA = new std::map<int,int>();
}
TestClass::~TestClass
{
mapA->clear(); // not necessary
delete mapA;
}
So, now I read all over the place at Stackoverflow: avoid pointers as often as possible
Currently I want to create the map without pointer and new (no need to delete the object by myself and less danger of getting some memory leak)!
class TestClass
{
public:
TestClass() : mapA() // this is also needed?
{};
private:
std::map<int,int> mapA;
};
Any further steps for correct creation of the map necessary?
Thanks for any help and/or clarification!
Nope that's it, and you don't need to explicitly initialize it in the constructor.
As zennehoy says, it is not necessary to initialize the map in the TestClass constructor.
Let me note a difference between the two implementations:
In the first one, the TestClass, as it is currently written, is not copyable without undesirable effects because the raw pointer to the dynamically allocated map is copied:
TestClass *A = new TestClass; // A has a map
TestClass *B = new TestClass(A); // B shares the map with A!
delete A; // this deletes A's map (in destructor)
delete B; // this deletes A's map again! wrong
In your second implementation, that does not happen because the map, and not just its address, is copied completely.
To solve that issue in your first implementation, you should use a shared pointer, or do the work yourself by implementing the operator= and the copy constructor. Or, if you want to really share the map between copied instances, you should implement a reference counting mechanism.

Does set::insert saves a copy or a pointer C++

does the function set::insert saves a pointer to the element or a copy of it. meaning, can I do the following code, or I have to make sure that the pointers are not deleted?
int *a;
*a=new int(1);
set<int> _set;
_set.insert (*a);
delete a;
*a=new int(2);
_set.insert (*a);
delete a;
I gave the example with int, but my real program uses classes that I created.
All STL containers store a copy of the inserted data. Look here in section "Description" in the third paragraph: A Container (and std::set models a Container) owns its elements. And for more details look at the following footnote [1]. In particular for the std::set look here under the section "Type requirements". The Key must be Assignable.
Apart from that you can test this easily:
struct tester {
tester(int value) : value(value) { }
tester(const tester& t) : value(t.value) {
std::cout << "Copy construction!" << std::endl;
}
int value;
};
// In order to use tester with a set:
bool operator < (const tester& t, const tester& t2) {
return t.value < t2.value;
}
int main() {
tester t(2);
std::vector<tester> v;
v.push_back(t);
std::set<tester> s;
s.insert(t);
}
You'll always see Copy construction!.
If you really want to store something like a reference to an object you either can store pointers to these objects:
tester* t = new tester(10);
{
std::set<tester*> s;
s.insert(t);
// do something awesome with s
} // here s goes out of scope just as well the contained objects
// i.e. the *pointers* to tester objects. The referenced objects
// still exist and thus we must delete them at the end of the day:
delete t;
But in this case you have to take care of deleting the objects correctly and this is sometimes very difficult. For example exceptions can change the path of execution dramatically and you never reach the right delete.
Or you can use smart pointers like boost::shared_ptr:
{
std::set< boost::shared_ptr<tester> > s;
s.insert(boost::shared_ptr<tester>(new tester(20)));
// do something awesome with your set
} // here s goes out of scope and destructs all its contents,
// i.e. the smart_ptr<tester> objects. But this doesn't mean
// the referenced objects will be deleted.
Now the smart pointers takes care for you and delete their referenced objects at the right time. If you copied one of the inserted smart pointers and transfered it somewhere else the commonly referenced object won't be delete until the last smart pointer referencing this object goes out of scope.
Oh and by the way: Never use std::auto_ptrs as elements in the standard containers. Their strange copy semantics aren't compatible with the way the containers are storing and managing their data and how the standard algorithms are manipulating them. I'm sure there are many questions here on StackOverflow concerning this precarious issue.
std::set will copy the element you insert.
You are saving pointers into the set.
The object pointed at by the pointer is not copied.
Thus after calling delete the pointer in the set is invalid.
Note: You probably want to just save integers.
int a(1);
set<int> s;
s.insert(a); // pushes 1 into the set
s.insert(2); // pushes 2 into the set.
Couple of other notes:
Be careful with underscores at the beginning of identifier names.
Use smart pointers to hold pointers.
Ptr:
std::auto_ptr<int> a(new int(1));
set<int*> s;
s.insert(a.release());
// Note. Set now holds a RAW pointer that you should delete before the set goes away.
// Or convert into a boost::ptr_set<int> so it takes ownership of the pointer.
int *a;
*a=new int(1);
This code is wrong because you try to use the value stored at address a which is a garbage.
And, every stl containers copy elements unless you use move semantics with insert() and push_back() taking rvalue references in C++0x.

Storing local variable in std::map

I have a class Message and a class Cache.
In Message::processMessage() fn. I create a instance of another class CacheRef(not shown below.)
then I call Cache::cacheData(cacheRef)
Now, in Cache class, I have a map which has its key as CacheReference. I store the ref that I passed to cacheData fn. in this map.
class Message
{
private:
Key m_key;
public:
void processMessage(int a, int b, Cache *pCache)
{
CacheRef ref(a, b, m_key); //CacheRef is a class defined in same file
//some char *data - do processing an dfill it!!
pCache->cacheData(ref, data);
}
}
class Cache
{
public:
void cacheData(CacheRef &ref, const char* data)
{
CacheDir *dir;
std::map<<CacheRef, CacheDir*>::iterator it = m_dirs.find(ref);
if(it == m_dirs.end())
{
dir = new CacheDir();
m_dirs.insert(ref, dir);
}
}
std::map<CacheRef, CacheDir*> m_dirs; //CacheDir is some class defined in the same file
}
Now, the code is working absolutely fine. But I have this concern(not sure!!) that I am storing some local variable in map, which which cease to exist as soon as processMessage()fn. exits. So, am I accessing some invalid memory, is it just by luck that this code is working.
If this is wrong, what is the best way to achieve this behaviour?
I don't have boost on my system, so can't use shared_ptr for anything.
Because the 1st template parameter is a CacheRef (and not a reference or pointer to a CacheRef) then ref will be copied into the map when you do the insert. Hence, you won't be storing a reference to a local stack variable.
As long as there is an appropriate copy constructor or assignment operator for CacheRef then this will work ok.
As Stephen Doyle pointed out, you are actually storing a copy of the CacheRef in the map, not a reference to the one passed to the cacheData() method.
Whether this causes a problem or not depends on the definition of the CacheRef class. If, for example, a CacheRef holds a pointer or a reference to the Key passed to the constructor, you will end up with an invalid pointer once the Message instance is destroyed.
By the way, since you are storing dynamically allocated objects of CacheDir in Cache::m_dirs, you should make sure to delete all values in the map in the Cache::~Cache() destructor to avoid memory leaks.