Storing local variable in std::map - c++

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.

Related

Changing class member address

Summarizing:
Is it possible to change a class member property memory address?
What lead me to do this question:
I'm not sure if what I want to do will lead into my expected behavior, and even if it works as expected, if it is what I should do.
So, I have a member property which I want to read from disk, say for instance that it is prop from MyClass. The routine I have that read the property from disk getVarOnFile I do not have access to the implementation, but it uses a pointer to the type to fill the value on the file.
In the documentation it says that if the property does not exists, the pointer may be null, although I am unsure if it will set it to null or it is because it expects that I enter null pointer to the function.
Because of that, instead of using the destVar pointer itself, I use a localVar pointer, and then set the destVar to localVar.
But I am not sure if I should do that, it seems that this will segment memory, where most of the members are close in the memory, but not this one that I set to the memory place reserved by the file.
void readHelper(const char* propOnFile, float*& destVar)
{
// check if propOnFile exists
float *localVar = nullptr;
getFileOnVar(propOnFile,localVar);
if ( localVar != nullptr){
destVar
}
}
class MyClass {
private:
float prop;
public:
static MyClass *read(const char* file){
readHelper("prop",&(this->prop));
}
};
I am not sure what would happen to the original memory place reserved for the original class property member, which was replaced to a new place. It will be freed or this would be memory leak?
Changing class member address
If I understand your question correctly, that is not possible.
Say you have:
struct Foo
{
int a;
};
Foo f;
Once you have created f, the address of f and f.a are not changeable. You can only modify their values.

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.

Class Member Variable not Saving State

I'm new to C/C++, so people excuse me if this is a noob question.
I have a controller class, which has a private member variable which is a vector<Contact> contacts. The class has methods which allow us to change the state of this vector (standard CRUD operations).
The class has a public method called get_contacts() which returns this private vector of objects:
std::vector<Contact> Contacts_Controller::get_contacts() const {
return this->contacts;
}
I have a method which adds the contact to this private vector via push_back(). The issue is that if the add_contact() method uses the accessor method, then the class variable does not get updated:
void Contacts_Controller::add_contact(const Contact &contact) {
this->get_contacts().push_back(contact);
}
I assume this is a memory issue, perhaps I have some issues with my use of const or I'm not correctly using references, because the following code works exactly as expected:
void Contacts_Controller::add_contact(const Contact &contact) {
this->contacts.push_back(contact);
}
Using either method won't return any errors, but if I then request the vector of contacts after using the first method it'll be empty, whereas the second method correctly adds data to the vector.
Again, sorry if this is a noob question, but I'm completely stumped, and I wouldn't know what to search for! How would I fix this referencing issue?
"The class has a public method called get_contacts() which returns this private vector of objects"
Nope, it returns a copy. You need to return by reference to get the actual member:
std::vector<Contact>& Contacts_Controller::get_contacts(){
return this->contacts;
}
Note that I removed the const, otherwise you'd have to mark the return as const also and you wouldn't be able to modify it.
The issue is that your function is returning the vector by value, which means the calling function gets a copy.
When you call push_back() on it, only the copy gets the extra member.
Your function could return a reference. Then your user can call push_back(). If it is a member variable of your class and you are returning a non-const reference (which you must to allow the modification) your function will probably also be non-const.
Thus:
std::vector<Contact> & Contacts_Controller::get_contacts() // not const
{ return this->contacts; }
You can also have a read-only version, as an overload.
std::vector<Contact> const & Contacts_Controller::get_contacts() const
{ return this->contacts; }
This version allows read-only access to the contacts and can also be a const member function.
When you call get_contacts, you return a copy of this->contacts. If you want to share a vector, either return a reference to it (but you must be sure that your reference will stay valid so your class instance won't be moved or copied), or use a smart pointer to dynamically allocate the space for your vector and extend its lifetime until the last reference to it has been destructed. In this case use a member with type std::shared_ptr<std::vector<Contact> >

C++ std::vector in class, function crash on returning value and size growing in foreach

I'm writing a content management system to avoid duplicates of the same loaded texture in my game engine. The following is the function for retrieving content from the previously loaded in objects or to load in a new object if none is available.
template <class T>
T* GetContent(const char* path) {
// Check if it already exists, if yes return it
for (ContentEntry& entry : m_ContentList) {
// Same Type?
if (strcmp(entry.Type, T::GetType()) == 0)
// Same Path?
if (strcmp(entry.C->GetPath(), path) == 0)
return (T*)entry.C;
}
// Since it doesn't exist, create it
ContentEntry contentEntry (
T::GetType(),
(Content*)new T(path));
// Add it to the list
m_ContentList.push_back(contentEntry);
// And Return it
return (T*)contentEntry.C;
}
And this is the struct used to store content entries and the vector they're stored in.
struct ContentEntry {
const char* Type;
Content* C;
ContentEntry(const char* type, Content* c) :
Type(type),
C(c)
{ }
~ContentEntry() {
delete C;
}
};
std::vector<ContentEntry> m_ContentList;
Whenever this function tries to return the value, the app crashes. When I change contentEntry to a pointer (updating the code around it appropriately) it returns with no problem but I have to change the entire vector to point to ContentEntry pointers and then manually delete them which I would like to avoid if possible. How could I make this function work correctly?
Additionally, when using the pointer and stepping through the foreach loop, the vector seems to grow drastically for no clear reason, how can I stop this from happening?
Edit: For now fixed the crashing problem which I'll later refine, but the vector growing out of control is still there.
Edit2: The vector growing seems to just disappear after exiting the function so I'm just gonna mark something as answer.
From the looks of it, you are deleting something which looks like a string using delete rather than delete[]. Of course, this assumes that the string was allocated in the first place. Based on your comment you try to delete a string literal which causes undefined behavior at that point.
That said, please note that you are slicing your object when you insert it into the vector and, more importantly, you don't get a deep copy of the ContentEntry members (this type is lacking a copy constructor and probably an assignment operator). Thus, after inserting the ContentEntry into your std::vector<ContentEntry> the newly allocated object is gone. Another interesting bit is that you cast your T* to a Content*. The allocate object is deleted through a pointer to Content. Thus, either the cast is unnecessary (and hopefully your type Content has a virtual destructor) or things will start going bad at that point.
// Since it doesn't exist, create it
ContentEntry contentEntry (
T::GetType(),
(Content*)new T(path));
// Add it to the list
m_ContentList.push_back(contentEntry);
// And Return it
return (T*)contentEntry.C;
Your problem is in these three lines, in combination with ContentEntry not having a copy constructor (and copy assignment).
in the first part you create a ContentEntry instance
then you push_back a copy of that instance. this copy will point to the same T instance that the original ContentEntry pointed to.
finally, the function exits, return the pointer-to-T. But at the exit, your local copy contentEntry is destroyed, which will delete the T-instance that the returned pointer points to.
In essence, you are not following the Rule of Three and are being punished for it.
You need a copy constructor for ContentEntry.

how can I check if an object exists in C++

I am trying to write a function that will check if an object exists:
bool UnloadingBay::isEmpty() {
bool isEmpty = true;
if(this->unloadingShip != NULL) {
isEmpty = false;
}
return isEmpty;
}
I am pretty new to C++ and not sure if my Java background is confusing something, but the compiler gives an error:
UnloadingBay.cpp:36: error: no match for ‘operator!=’ in ‘((UnloadingBay*)this)->UnloadingBay::unloadingShip != 0’
I can't seem to figure out why it doesn't work.
Here is the declaration for class UnloadingBay:
class UnloadingBay {
private:
Ship unloadingShip;
public:
UnloadingBay();
~UnloadingBay();
void unloadContainer(Container container);
void loadContainer(Container container);
void dockShip(Ship ship);
void undockShip(Ship ship);
bool isEmpty();
};
It sounds like you may need a primer on the concept of a "variable" in C++.
In C++ every variable's lifetime is tied to it's encompassing scope. The simplest example of this is a function's local variables:
void foo() // foo scope begins
{
UnloadingShip anUnloadingShip; // constructed with default constructor
// do stuff without fear!
anUnloadingShip.Unload();
} // // foo scope ends, anything associated with it guaranteed to go away
In the above code "anUnloadingShip" is default constructed when the function foo is entered (ie its scope is entered). No "new" required. When the encompassing scope goes away (in this case when foo exits), your user-defined destructor is automatically called to clean up the UnloadingShip. The associated memory is automatically cleaned up.
When the encompassing scope is a C++ class (that is to say a member variable):
class UnloadingBay
{
int foo;
UnloadingShip unloadingShip;
};
the lifetime is tied to the instances of the class, so when our function creates an "UnloadingBay"
void bar2()
{
UnloadingBay aBay; /*no new required, default constructor called,
which calls UnloadingShip's constructor for
it's member unloadingShip*/
// do stuff!
} /*destructor fires, which in turn trigger's member's destructors*/
the members of aBay are constructed and live as long as "aBay" lives.
This is all figured out at compile time. There is no run-time reference counting preventing destruction. No considerations are made for anything else that might refer to or point to that variable. The compiler analyzes the functions we wrote to determine the scope, and therefore lifetime, of the variables. The compiler sees where a variable's scope ends and anything needed to clean up that variable will get inserted at compile time.
"new", "NULL", (don't forget "delete") in C++ come into play with pointers. Pointers are a type of variable that holds a memory address of some object. Programmers use the value "NULL" to indicate that a pointer doesn't hold an address (ie it doesn't point to anything). If you aren't using pointers, you don't need to think about NULL.
Until you've mastered how variables in C++ go in and out of scope, avoid pointers. It's another topic entirely.
Good luck!
I'm assuming unloadingShip is an object and not a pointer so the value could never be NULL.
ie.
SomeClass unloadingShip
versus
SomeClass *unloadingShip
Well, you don't have to write so much code to check if a pointer is NULL or not. The method could be a lot simpler:
bool UnloadingBay::isEmpty() const {
return unloadingShip == NULL;
}
Plus, it should be marked as "const" because it does not modify the state of the object and can be called on constant instances as well.
In your case, "unloadingShip" is an object of class "UnloadingShip" which is not dynamically allocated (except when the whole class "UnloadingBay" is allocated dynamically). Thus, checking if it equals to NULL doesn't make sense because it is not a pointer.
For checking, if an object exists, you can consider going this way:
create a pointer to your object:
someClass *myObj = NULL // Make it null
and now where you pass this pointer, you can check:
if(!myObj) // if its set null, it wont pass this condition
myObj = new someClass();
and then in case you want to delete, you can do this:
if(myobj)
{
delete myObj;
myObj = NULL;
}
so in this way, you can have a good control on checking whether your object exists, before deleting it or before creating a new one.
Hope this helps!