Put a stack params to a map? - c++

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

Related

Returning std::vector in std::map

I'm trying to return an std::vector that is inside a map but I don't seem to get it right.
The code below is what I have tried and the one further below is the test it's supposed to pass
namespace grade_school {
class school {
public:
inline std::map<int, std::vector<std::string>> roster () {return
MySchool;}
void add (std::string studentname , int grd);
std::vector<std::string>& grade (int grd);
private:
std::map<int,std::vector<std::string>> MySchool;
};
}
void grade_school::school::add (std::string studentname, int grd){
MySchool[grd].push_back(studentname);
}
std::vector<std::string>& grade (int grd) {
return MySchool[grd];
};
TEST_CASE("grade_returns_the_students_in_that_grade_in_alphabetical_order")
{
grade_school::school school_;
school_.add("Franklin", 5);
school_.add("Bradley", 5);
school_.add("Jeff", 1);
const auto actual = school_.grade(5);
const vector<string> expected{"Bradley", "Franklin"};
REQUIRE(expected == actual);
}
I expected that the return type would be the vector contained inside the map but the compiler error i got was error C2065: 'MySchool': undeclared identifier
I expected that the return type would be the vector contained inside the map.
This leaves room for interpretation, depending on how you interpret this sentence, this already is the case – or not...
std::vector<std::string> grade(int grd);
In C++, we speak of returning 'by value'. You get exactly the values that are in the map. But returning by value means, you get a copy of. If you now modify the returned value, you actually modify the copy, but not the vector in the map.
std::vector<std::string>& grade(int grd);
// ^ note the ampersand!
The ampersand denotes that we just return a reference to the vector in the map (in this case, it behaves similar to a pointer), and via the reference, you now can modify the vector in the map directly:
grade(7).push_back("seven");
For completeness: pointer:
std::vector<std::string>* grade(int grd) { return &mySchool[grd]; }
// need address-of operator: ^
grade(7)->push_back("seven");
Usually, one prefers the reference, but the pointer can be useful in some specific scenarios, e. g. you can return a nullptr (not possible with references) to denote that there is no suitable vector for some specific input.
In some use-cases, you might not want to allow to modify the vector returned. Returning by copy avoids that, but a copy might be expensive. To avoid copies and still disallow modifications, you can return by const reference:
std::vector<std::string> const& grade(int grd);
Admitted, a user might const_cast the const away, but then it's the user breaking a contract (returning something as const means tealling: 'Don't modify it, if you do so, your fault!').
Use reference type for return value:
std::map<int,std::vector<std::string>> MySchool;
std::vector<std::string>& grade (int grd) {
return MySchool[grd];
}
Using reference with containers and other enough big data objects is preferable due to perfomance. This minimize memory operations caused by data duplications.
Non const reference approach is good if you plan to modify vector outside of method or object which incapsulate this map. This is simple solution which always give you actual data and allow to modify.
Better approach is to incapsulate modifications of data in methods of class (or api function set) to avoid unexpected and unverified modificaions by user. Use return type const reference for that like this:
const std::vector<std::string>& grade (int grd)
If you really need to have a copy of array for that case you can do this simple by expression sintax. For example assign to value instead of reference:
// ref - is const reference to some array returned by function
const std::vector<std::string>& ref = grade(10)
// array - is a full copy of array pointed by reference returned from function
std::vector<std::string> array = grade(10)
When you return reference to container or some data you need to understand problem that user code save reference and it may be used multiple times. And during that time and dependent from how your container or your code manage data in memory it may bring to problem. So it is possible case when data at memory referenced by prevously returned reference is released. Returned before reference become refer to wrong memory. But user code may use that reference and bring to access violation or some undefined behaviour.
Even if your object guarantee that reference returned by method will exists during all object life time. Reference may be saved in the user code more long time than object itself. So there is no guarantee at all.
However we may use simple rule just to not save a reference in variable. Just use data in same expression with function call always:
grade(10).length()
std::accumulate(grade(10).begin(), grade(10).end(), 0)
However user may do not know or forget this rule.
And of course your function must always return reference to existing data. For example if no such array at specified index you need to create appropriate array at specified index and use it for return.
And of course function must not return reference to any non-static variable declared in function body because all of them will be destroyed after function return and before return value will be used. Which guarantee crash of aplication. Use return by value for that case.

c++ why do i need 'new' when creating objects in a loop

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;
}

How to optimize std::map insert() function?

The best way to explain what I'm trying is accomplish is with this example (compiled with Visual Studio 2008 SP1):
struct ELEMENT1{
//Its members
ELEMENT1()
{
//Constructor code
}
~ELEMENT1()
{
//Destructor code
}
};
std::map<std::wstring, ELEMENT1> map;
std::pair<std::map<std::wstring, ELEMENT1>::iterator, bool> resIns;
ELEMENT1 element;
std::wstring strKey;
for(size_t i = 0; i < numberRepetitions; i++)
{
//Do processing
//...
//set 'strKey'
//Insert new element into the map first
resIns = map.insert(std::pair<std::wstring, ELEMENT1>(strKey, element)); //This line calls ELEMENT1 constructor & destructor twice
//Then fill out the data
fill_in_data(resIns.first->second);
}
BOOL fill_in_data(ELEMENT1& outInfo)
{
//Fill in 'outInfo' -- MUST be in its own function
//...
}
My goal is to optimize this code, and thus I did the following:
Moved ELEMENT1 element construction/destruction outside of the loop.
I'm inserting the element into the map and then attempt to fill it out using the pointer to the inserted element instead of constructing new element, then filling it out, then copying it into the map, and then destroying it. (At least that was the plan.)
But when I compile this for a Release build and check the assembler code, I can see that the C++ line with map.insert() function calls ELEMENT1 constructor twice! and then twice its destructor. So the following machine code is just for that map.insert() line:
So I'm obviously not seeing something here.
Can someone suggest what's going on in that compiled code & if it's possible to optimize it?
The reason you have 2 constructor calls is because what you are passing to insert does not match what it need. std::map::insert takes a const value_type& and value_type for a map is
std::pair<const key_type, element_type>
^^^^^ this is important
So, since they do not match you construct one element when you use
std::pair<std::wstring, ELEMENT1>(strKey, element)
and then the compiler calls the copy constructor to convert that into a
std::pair<const std::wstring, ELEMENT1>
A quick fix is to change the code to
std::pair<const std::wstring, ELEMENT1>(strKey, element)
Which leaves you with one temporary that is constructed and destructed. You can also do as zett42 suggests in their answer to avoid the creation of the temporary entirely.
resIns = map.insert(std::pair<std::wstring, ELEMENT1>(strKey, element));
You are constructing a temporary std::pair whose member second is a ELEMENT1. This causes the copy constructor of ELEMENT1 to be called.
The 2nd call to the copy constructor of ELEMENT1 is when std::map::insert() creates a new element in the map that will be initialized by the temporary std::pair.
You can avoid the duplicate constructor call caused by the temporary by using std::map::operator[] instead:
ELEMENT1& resIns = map[ strKey ];
fill_in_data( resIns );
If strKey doesn't already exist in the map, an ELEMENT1 will be default-constructed directly within the map and a reference to the new object will be returned. The constructor will be called exactly one time.
If strKey already exists in the map, a reference to the existing object will be returned.
You should use emplace to avoid creation on temp objects:
resIns = map.emplace
(
::std::piecewise_construct
, ::std::forward_as_tuple(strKey)
, ::std::forward_as_tuple()
);
A good reason to switch to newer VS version.

Delete custom object from vector

I have the following code to store objects of type LVA in a vector.
I've implemented a method for adding a LVA Object to the vector.
First question: Is it okay if i use a reference to the LVA object in this method?
Now i want to implement a method for the deletion of a LVA object. I want to have the following method signature: void RemoveLVA(LVA& lva). How can I implement this method? i.e. How can i find the right object in the vector to delete it?
Manager.h
class Manager {
public:
Manager();
Manager(const Manager& orig);
virtual ~Manager();
vector<LVA> GetLvas() const;
void AddLva(LVA& lva);
private:
vector<LVA> lvas;
};
Manager.cpp:
#include "Manager.h"
Manager::Manager() {
}
Manager::Manager(const Manager& orig) {
}
Manager::~Manager() {
}
vector<LVA> Manager::GetLvas() const {
return lvas;
}
void Manager::AddLva(LVA& lva) {
lvas.push_back(lva);
}
Is it okay if i use a reference to the LVA object in AddLva(LVA& lva)?
Yes, std::vector will have a copy of your original object after push_back() is complete.
How can I implement RemoveLVA(LVA& lva)?
You will need to find the object in your vector. (You may use std::find() if operator==() is defined for LVA.) Then invoke the erase() function for your vector.
Is it okay if i use a reference to the LVA object in this method?
No problem. You are actually doing a copy by a push_back operation.
Without knowing more about LVA, it's hard to be precise, but
you probably want a const reference for AddLva, since you're
not modifying it in the function (and you may want to pass
a temporary).
For removal: you'll have to define some sort of
equivalence function over LVA is you want to remove a matching
element. This can be LVA::operator==, or some other object or
function: in the first case, you use std::find to find the
position, and in the second, std::find_if. If the vector can contain
more than one matching element, you might want to look into std::remove or std::remove_if.
Your insertion method is right, but don't forget that only a copy from your LVA object is inserted in the vector. it means than in the RemoveLVA method, you will need to use somthing like an operator== overload to std::find the vector's object which matches your parameter, and then deleting it.
You may prefer to insert pointers or std::shared_ptr of LVA in your vector. No object copy will then occur, and the search of an object will fall back to a comparison of pointer, given you keep the pointer somewhere else in your program (2 objects with same contents and different stack or heap locations would then compare to false).
Yes, AddLva can take a reference, although it would typically take a const reference:
void Manager::AddLva(const LVA& lva) {
lvas.push_back(lva);
}
Note however that if you are expecting that the reference is added to the vector, this isn't doing that. A copy of the object lva refers to is made, and that copy is added to the vector.
Yes it's Ok to pass the LVA object as ref in your add and remove method. You could also declare those parameters as 'const' because I suppose add and remove will not affect the object .
for the remove method
void RemoveLVA(const LVA& lva)
{
std::vector<LVA>::iterator position =
std::find(lvas.begin(), lvas.end(), lva);
if (position != lvas.end())
{
lvas.erase(position);
}
}
but LVA class must have an == operator.
good luck.

How are objects passed to functions C++, by value or by reference?

Coming from C#, where class instances are passed by reference (that is, a copy of the reference is passed when you call a function, instead of a copy of the value), I'd like to know how this works in C++. In the following case, _poly = poly, is it copying the value of poly to _poly, or what?
#include <vector>
using namespace std;
class polynomial {
vector<int> _poly;
public:
void Set(vector<int> poly);
};
void polynomial::Set(vector<int> poly) {
_poly = poly; <----------------
}
poly's values will be copied into _poly -- but you will have made an extra copy in the process. A better way to do it is to pass by const reference:
void polynomial::Set(const vector<int>& poly) {
_poly = poly;
}
EDIT I mentioned in comments about copy-and-swap. Another way to implement what you want is
void polynomial::Set(vector<int> poly) {
_poly.swap(poly);
}
This gives you the additional benefit of having the strong exception guarantee instead of the basic guarantee. In some cases the code might be faster, too, but I see this as more of a bonus. The only thing is that this code might be called "harder to read", since one has to realize that there's an implicit copy.
This will do a shallow-copy of the vector of ints. This will generally work as you would expect (_poly will end up containing the same values as poly).
You would see some strange behaivor if you had pointers (as they would be copied by value).
In general, you would want to pass that parameter by const reference:
void polynomial::Set( const vector<int>& poly )
In this case, passing by const reference will not affect the outcome and will be more efficient since it will eliminate an unneeded copy of the vector being passed into the method.
This will copy the entire vector. Assignment is by value in C++. If you are assigning a pointer, the pointer value is assigned. References may not be reassigned to refer to another object once initialized, so assignment of them alters the referent object.
The copy operator for vectors will copy the contents of the vector over.
There are three possibilities:
Pass by value
void someFunction(SomeClass theObject);
Pass a pointer
void someFunction(SomeClass *theObject);
Pass by reference
void someFunction(SomeClass &theObject);
Your vector will be copied.
What's actually going on is that the "=" operator of vector has been overloaded to do the actual copy.
Yes, the line you point to is copying the entire vector. Furthermore, there will be a copy on the function call, as well, since that's not const.
Basically, if the vector has any size to it, this is VERY expensive.
Unless you assign or pass a parameter by reference (using the & prefix) you are passing by value. For classes, this means that a copy of the object is constructed using either a supplied or implicitly generated (shallow) copy constructor for the type. This can be expensive - and is often undesirable.
In your example, the vector is copied twice - once when it is passed as a parameter to the Set() method, and again when it is assigned to the _poly member.
You could avoid the first copy by passing the vector by reference:
void polynomial::Set(const vector<int>& poly) // passes the original parameter by reference
{
_poly = poly; // still makes a copy
}