I have a program that reads lines from a file looking like this:
...
name1 (123)
name2 (345)
...
It then stores each line as an object of MyClass in a map called namelist. The key of namelist is the name of the object the value is the object itself. In the first version of the code namelist was of type map< string,MyClass >, and the objects were created in the loop as MyClass obj; (and ofc all '->' were '.'. But that only created a map of equal objects, why? I've read that you rarely need to use 'new' on objects in c++. Why do i need it here?
ifstream myfile ("input.txt");
map<string,MyClass*> namelist;
string line;
while ( getline(myfile,line) ) {
istringstream iss(line);
string word;
while (iss>>word) {
MyClass* obj = new MyClass;
obj->name = word;
iss>>word;
sscanf(word.c_str(),"(%d)",&obj->number);
namelist.insert( pair<string,MyClass*>(obj->name,obj) );
}
}
myfile.close();
You don't need new. Just do map<string,MyClass> namelist and MyClass obj; (default constructor w/ stack allocation)
new is for allocating objects on the heap and the data must be explicitly deallocated with delete. Instead, simply make your map hold MyClass objects instead of pointers to such objects. The map object will copy the object onto the heap and take care of deallocation for you.
.
You don't need new as std::map already takes care of dynamic allocation.
std::map<std::string, MyClass> namelist;
You don't even need to create a temporary variable first and insert that into the map, which creates an unneccessary copy. Instead, creating the object directly within the map will be more efficient.
The following technique requires MyClass to have a default constructor only. Apart from being more efficient, this technique also enables you to use classes that do not have a copy constructor (e. g. std::ifstream) as map values.
To get the same semantics as std::map::insert() an explicit call to find() is required. If you can live with the fact that duplicate keys overwrite existing objects in the map, you can simplify the code by removing the if.
// Check if the key does not exist yet.
if (namelist.find(word) == namelist.end()){
// Default-construct object within map and get a reference to it.
MyClass& obj = namelist[word];
// Do something with inserted obj...
obj.name = word;
}
If your class has a constructor with one or more parameters, that you would like to call during in-place construction, you need a C++11 feature, namely std::map::emplace().
// Construct MyClass within the map, if the key does not exist yet.
// Pass arguments arg1, arg2, argN to the constructor of MyClass.
auto res = namelist.emplace(arg1, arg2, argN);
if (res.second){ // new object inserted?
// Get a reference to the inserted object.
MyClass& obj = *res.first;
// Do something with inserted obj...
obj.name = word;
}
Related
Cheers! I have a question that regards something that I think might be considered "bad practice", but I would like to make sure.
Let's suppose I have a class that contains inside it a vector, in which I want to store Objects of e.g. Students, which are classes that only hold some integers, nothing fancy inside them, such as pointers or else. When I create the superclass e.g. class A {}; which holds this vector, I want to initialize the vector with some students for example, when the constructor is called. So I create a temporary object e.g. Student to_enter;, which I then push inside the vector. This vector will be used by other classes, in order to remove or add even more students. I know that the temporary object will go out of scope, when the scope of the constructor is ended, but I think the object is created by copy construction when put inside the vector, so I think that there is no problem when a future function will try to change these Objects (that have been created by copy construction).
However, is this correct? Or am I just lucky that there has not been an error so far? I guess what I am asking, is if the object inside the vector affect the lifetime of the object, so that it can be changed later with no problem. Also, is this a bad practice and I will have to put pointers inside, or is this not necessary? Thanks =)
So you have
class Student{
.....
}
std::vector<Student> vec;
and wonder what happens if you do
void func(std::vector &vec){
Student s1;
vec.push_back(s1);
}
func(vec);
will vec contain a valid Student object
Yes, s1 will be copied into vec.
But dont do this, its very inefficient for non trivial objects. std::shared_ptr is you friend. Do this instead
class Student{
.....
}
std::vector<std::shared_ptr<Student>> vec;
void func(std::vector &vec){
auto s1 = std::make_shared<Student>();
vec.push_back(s1);
}
func(vec);
Now your student objects are not being copied. Instead you have them on the heap, nicely managed for you.
BTW I would do
using PStudent = std::shared_ptr<Student>;
std::vector<PStudent> vec;
just to reduce the typing and visual noise
I view this code at CCArmatureDataManager.cpp 253 line. RelativeData is a struct.Here, put a stack param into a map. Why, no problem?? Is there someone explain this to me? thx!!!
struct RelativeData
{
std::vector<std::string> plistFiles;
std::vector<std::string> armatures;
std::vector<std::string> animations;
std::vector<std::string> textures;
};
void CCArmatureDataManager::addRelativeData(const std::string& configFilePath)
{
if (_relativeDatas.find(configFilePath) == _relativeDatas.end())
{
_relativeDatas[configFilePath] = RelativeData();
}
}
In the expression
_relativeDatas[configFilePath] = RelativeData()
The RelativeData() part creates a temporary default-constructed object.
The _relativeDatas[configFilePath] part calls std::map::operator[] which returns a reference to an object.
The assignment copies from the temporary object to the object whose reference the [] operator returned. In other words, the RelativeData copy assignment operator is called (the compiler will in most cases create one for you if you don't have one).
If there is no element with the key configFilePath, then the map will default construct one, and return a reference to it.
So what your code does is create two default-constructed objects of type RelativeData, and copies the contents from one to the other. It is, in maybe not so kind words, pretty much useless.
Looks like the function simply adds, if not already there, an empty struct to the _relativeDatas map ( which is most seemingly a std::map < std::String configFile, struct RelativeData > ), which can then be filled with data
I have objects of type MyClass stored as pairs <std::string, MyClass> in an STL Map. The std::string is a unique name for each MyClass object. I want every MyClass object to be instantiated only ONCE per name and thus destroyed only once at the end in my application. So I try to avoid invocation of copy constructors or default constructors, as they might invoke destruction. A MyClass object refers to some kind of ressource that shall be allocated/freed only once. I tried to use this code to create instances of MyClass, put them in my map and give a pointer to the just created instance back.
MyClass* FooClass::GetItem(std::string name)
{
MyClass* item = GetItemExists(name);
if (item == NULL)
{
item = &(*((this->myMap.insert(std::pair<std::string, MyClass>
(name, MyClass(name)))).first)).second;
}
return item;
}
Creation and insertion works this way. But the destructor of Class MyClass is called 3! times. Even the return item; statement invokes the destructor, as this is a pointer?! I thought this is impossible and must be forced by delete item?!
I thought an alternative is to store pointers MyClass* instead of objects in the map. Or is there a better alternative? I did not use myMap[name] = MyClass(name); to avoid copy/destruction, but I think insert doesnt make it better.
You need to emplace and piecewise construct the inserted element:
item = &(this->myMap.emplace(std::piecewise_construct,
std::forward_as_tuple(name),
std::forward_as_tuple(name)).first->second);
Simple question, I just want to initialize a map to be empty, instead of being a nullptr.
const std::map<std::string, std::string>* emptyDictionary;
I tried
const std::map<std::string, std::string>* emptyDictionary = {"", ""};
but obviously that's not right.
Thanks guys.
You forgot to make any map at all -- you just made a pointer! You can make the pointer point to a dynamically allocated map:
const std::map<std::string, std::string>* emptyDictionary
= new std::map<std::string, std::string>;
This map will be truly empty. If you add the initializer {{"", ""}}, which you may well do, then you don't actually have an empty map, but rather a map with one element which maps an empty string to an empty string.
Note that you can never modify your map through the const pointer, so it's a bit questionable why you'd want to do this.
Note also that wanton dynamic allocation is generally a poor programming style. There's almost surely a better way to do whatever you need to do, or, based on your comment, you're just grossly misunderstanding something: The best way to obtain a pointer is to take the address of an existing object:
std::map<std::string, std::string> m;
foo(&m); // pass address of m as a pointer
const std::map<std::string, std::string>* emptyDictionary
= new std::map<std::string, std::string>();
The default (empty) constructor of map will create an empty map http://www.cplusplus.com/reference/stl/map/map/.
Either declare the map with automatic allocation on the stack by just writing
std::map<std::string, std::string> emptyDictionary();
And send it to your function using the addres-off operator
yourfunction(&emptyDictionary);
However, if the dictionary will outlive the instance of it was created, you need to dynamically allocate it instead to avoid a call to its destructor.
const std::map<std::string, std::string>* emptyDictionary = new std::map<std::string, std::string>();
Then you dont need the address-of operator when calling your function.
yourfunction(emptyDictionary);
However, the responsibility of deallocation will then be yours. When you dont need the object any longer, you need to delete the object using the delete statement.
delete emptyDictionary;
I have two examples I have a question about. Let me explain via some code:
Question 1:
QStringList qsl(); // Create a list and store something in it
qsl << "foo";
QString test = "this is a test";
qsl = test.split(" ", QString::SkipEmptyParts); // Memory Leak?
What happens when I re-assign the qsl variable what happens to "foo" and the original data allocated on the first line?
Question 2:
class Foo
{
QStringList mylist;
void MyFunc(QStringList& mylist)
{
this->m_mylist = mylist;
}
void AddString(QString str)
{
mylist << str;
}
}
int main()
{
Foo f;
QStringList *qsl = new QStringList();
f.MyFunc(*qsl);
delete qsl;
f.AddString("this is a test"); // Segfault?
}
Here I'm passing a list by reference to a class which is then stored in said class. I then delete the original object.
It basically all comes down to what happens when you assign a QObject to a QObject. I assume a copy of the object is made, even if the object was passed in via reference (not via pointer of course, that would just be a pointer copy).
I also assume that something like QStringList performs a deepcopy...is this correct?
Assigning to a QStringList variable works the same as assigning to any other variable in C++. For objects, the assignment operator of the object on the left is called to copy the content of the object on the right into the object on the left. Usually this does just a memberwise assignment:
struct A {
int x;
QString y;
A& operator=(const A &other) {
// do the assignment:
x = other.x;
y = other.y;
return *this;
}
};
The object on the left of the assignment "adapts itself" to contain the same things as the object on the right. There is no new object allocated, just the existing one is modified.
If the class is more complicated and for example contains pointers to dynamically allocated data (like it is probably is the case for QStringList), the assignment operator might be more complicated to implement. But this is an implementation detail of the QStringList class and you should not have to worry about that. The QStringList object on the left of the assignment will be modified to be equal to the object on the right.
In Question 2 you assign an object to a member variable, which causes the object in the member variable to be modified so that it contains the same things as the object that is assigned to it. That this other object later is deleted doesn't matter to the member variable.
Semantically this is the same as when assigning simple integers:
int i, j;
i = j;
The memory where i is stored is modified, so that it contains the same value as j. What happens to j later on doesn't matter to the value of i.
What happens when I re-assign the qsl variable what happens to "foo" and the original data allocated on the first line?
You can't reassign qsl to something else within the same scope.
Once it goes out of scope the memory will be reclaimed in it's destructor.
You can put different data into qsl, in which case it will replace "foo", more memory might be allocated if necessary
edit: eg. you can't have
"QStringlist qsl;" Then in the same code block have "int qsl;"
You can replace the strings in qsl with a different list and the container will handle the memory for you
I also assume that something like QStringList performs a deepcopy
Yes, - actually it's a little more complicated, to save time/memory Qt will only do the copy when it needs to, ie when it changes. If you copy "a string" to lots of different string lists, Qt will just keep one copy and share it around, when one changes it will allocate a new copy for the changed one - it's called "copy on write" but happens automatically and you don't need to care.