Can anybody help me with an iterator problem? I'm having something like this:
class SomeClass{
public:
//Constructor assigns to m_RefPtr a new t_Records
//Destructor deletes m_RefPtr or decreases m_RefCnt
//Copy Constructor assigns m_RefPtr to new obj, increases m_RefCnt
bool Search(const string &);
private:
//Some private variables
struct t_Records{ //For reference counting
int m_RefCnt; //Reference counter
typedef vector<int> m_Vec;
typedef map<string, m_Vec> m_Map;
m_Map m_RecMap;
t_Records(void){
m_RefCnt = 1;
}
};
t_Records * m_RefPtr;
};
//Searchs through the map of m_RefPtr, returns true if found
bool SomeClass::Search(const string & keyword){
//How to create and use an iterator of m_Map???
return true;
}
As how I mentioned, I'm having troubles with creating (defining) map iterator outside of the struct. The map is initalized and contains some records. Thanks for your reply.
Like this:
// assuming m_RefPtr is properly initialized:
t_Records::m_Map::iterator it = m_RefPtr->m_RecMap.begin();
++it; // etc.
By the way, m_Map is a bad name for a type. By common convention, names prefixed with m_ are used for data members.
You can iterate like this
for (m_Map::iterator it = m_RecMap.begin(); it != m_RecMap.end(); ++it)
{
// do stuff with *it
}
Or even easier
for (auto it = m_RecMap.begin(); it != m_RecMap.end(); ++it)
{
// do stuff with *it
}
Related
Apologies if this is a trivial problem.
I'm trying to pass a multimap that has been put together with one class in a library to another class in that library in order to further manipulate the data there.
The code relates to a GUI written by other people and the classes here relate to two different tools in the GUI.
Very roughly speaking my code and what I'm after here is like this
class A
{
private:
std::multimap<int, double> mMap;
int anInt;
double aDouble;
***some more definitions***
public:
void aFunction(***openscenegraph node, a string, and a parser function***)
{
***a few definitions are declared and initialised here
during calculations***
***some code calculating data stuff that
passes bits of that data to mMap (including information
initialised within the function)***
}
}
class B
{
public:
void bFunction(***openscenegraph node and some other data***)
{
***I want to be able to access all the data in mMap here***
}
}
Can anyone make it clear to me how I can do this, please?
Edit: Added to clarify what i'm aiming for
//Edit by Monkone
//section below is akin to what I'm trying to do
class B
{
private:
std::multimap<int, double> mMapb;
public:
std::multimap<int,double> bFunction2(A::MultiMapDataType data)
{
return mMap;
}
void bFunctionOriginal()
{
***I want to be able to access all the data in mMap here***
***i.e. mMapb.bFunction2(mMap);***
***do stuff with mMapb***
}
}
However I can't get anything to actually do something like this
I won't be needing to work on the map, only get information from it.
You could then add a function to return a const reference to the map and functions for returning const iterators to A:
class A {
public:
typedef std::multimap<int, double> intdoublemap_t;
typedef intdoublemap_t::const_iterator const_iterator;
// typedef intdoublemap_t::iterator iterator;
private:
intdoublemap_t mMap;
public:
// direct access to the whole map
const intdoublemap_t& getMap() const { return mMap; }
// iterators
const_iterator cbegin() const { return mMap.begin(); }
const_iterator cend() const { return mMap.end(); }
const_iterator begin() const { return cbegin(); }
const_iterator end() const { return cend(); }
/*
iterator begin() { return mMap.begin(); }
iterator end() { return mMap.end(); }
*/
};
Now you can iterate over the map from the outside (from B):
void bFunction(const A& a) {
for(A::const_iterator it = a.begin(); it!=a.end(); ++it) {
std::cout << it->first << " " << it->second << "\n";
}
}
Or access the map directly:
void bFunction(const A& a) {
const A::intdoublemap_t& mref = a.getMap();
//...
}
The C++ private members cannot be accessed by other (non friend) classed.
The first solution it would be to keep mMap private (not polite to work on other classes members) and offer assessors over it.
class A
{
public:
typedef std::multimap<int, double> MultiMapDataType;
private:
MultiMapDataType mMap;
***some more definitions***
public:
const MultiMapDataType& getConstMMap() const;
MultiMapDataType getMMap();
}
class B
{
public:
void bFunction(A::MultiMapDataType data)
{
***I want to be able to access all the data in mMap here***
}
void bFunction2(const A::MultiMapDataType& data)
{
***I want to be able to access all the data in mMap here***
}
}
A a;
B b;
b.bFunction(a.getMMap());
b.bFunction2(a.getConstMMap());
However, from architectural point, if you have multiple members that you need to share you should move them all in a new structure/class that encapsulates that functionality.
I have class CStudent and class CStudentGroup which has one member set<CStudent>. I populate the set of an object from the class CStudentGroup. I want to iterate this set and print via the getter of the CStudent class the points of all the students in the set. I do this by assigning the set to a new one. Then I iterate the set with an iterator it. However the compiler gives an error *the object has type qualifiers that are not compatible with the member function CStudent::getP; object type is const CStudent* I would like to ask how can I do this? Thank you in advance.
#include <iostream>
#include <string>
#include <set>
using namespace std;
class CStudent {
string m_strFN;
int m_iPoints;
public:
void setP(int p) {
m_iPoints = p;
}
void setFN(string f) {
m_strFN = f;
}
int getP() {
return m_iPoints;
}
string getFN() {
return m_strFN;
}
CStudent() {
m_strFN = "123456789";
m_iPoints = 70;
}
CStudent(const CStudent& stud) {
m_strFN = stud.m_strFN;
m_iPoints = stud.m_iPoints;
};
CStudent(int p) {
m_iPoints = p;
}
};
class CStudentGroup {
set<CStudent> m_setStudents;
public:
CStudentGroup(const CStudentGroup& grp) {
m_setStudents = grp.m_setStudents;
};
CStudentGroup(set<CStudent> st) {
m_setStudents = st;
}
CStudentGroup() {
CStudent s1(50), s2, s3(s2);
m_setStudents.insert(s1);
m_setStudents.insert(s2);
m_setStudents.insert(s3);
}
set<CStudent> gets() {
return m_setStudents;
}
};
int main()
{
CStudentGroup group;
set<CStudent> stt = group.gets();
for (set<CStudent>::iterator it = stt.begin(); it != stt.end(); it++) {
cout << it->getP() << endl;
}
}
std::set stores keys as constant value, as a change of a key can be a cause of change to its position in red-black tree (typical std::set implementation).
In other words, your CStudent object are considered const or unchangeable.
It's possible to problem here using std::set::const_iterator as a type of iterator inside the loop in combination with std::set::cbegin() and std::set::cend() calls.
Another possible solution is to use foreach-loop:
for (CStudent const& student : stt)
std::cout << student.getP() << '\n';
Moreover, you would need to change CStudent::getP() declaration to be a constant method.
Objects inside a std::set are always const. That is to protect them, in case you decide you change any key field, the sorting order changes and the set invariant is broken.
So basically the set<CStudent>::iterator is a const_iterator and you get a const CStudent& reference. Since your CStudent::getP is not a const member function, you cannot use it.
Solution, make it const:
int getP() const {
return m_iPoints;
}
Naturally, you want to mark as const any function that does not change the contents of your object, not only the ones std::set requires you to do so. This is sometimes called const-correctness and is always a good practice.
I have a class with a private member type with a getType in it, in a second class I have a vector of such class that I can add to as many classes as I want, now want I want to do is if I was given a "Type" I want to remove the whole Object from such vector by finding that object using that string and erase it. I have tried the way below but did not work, also tried iterators and templates yet none seem to work. * This is simplified for the sake of it*
class AutoMobile{
private:
string type;
public:
AutoMobile(string type){
this->type = type;
}
string getType(){return type;}
};
class Inventory{
private:
vector<AutoMobile> cars;
public:
void removeFromInventory(string type){ // No two cars will have the same milage, type and ext
AutoMobile car("Ford");
cars.push_back(car);
for( AutoMobile x : cars){
cout<<x.getType();
}
for( AutoMobile x : cars){
if(x.getType() == "Ford"){
cars.erase(*x); // Problem i here, this does not work!
}
}
}
};
int main(void) {
Inventory Inven;
Inven.removeFromInventory("Ford");
return 0;
}
Use of range for loop is not appropriate when you intend to remove items from a std::vector. Use an iterator instead.
vector<AutoMobile>::iterator iter = cars.begin();
for ( ; iter != cars.end(); /* Don't increment the iterator here */ )
{
if ( iter->getType() == "Ford" )
{
iter = cars.erase(iter);
// Don't increment the iterator.
}
else
{
// Increment the iterator.
++iter;
}
}
You can simplify that block of code by using standard library functions and a lambda function.
cars.erase(std::remove_if(cars.begin(),
cars.end(),
[](AutoMobile const& c){return c.getType() ==
"Ford";}),
cars.end());
You can use remove_if
cars.erase(std::remove_if(cars.begin(),
cars.end(),
[=](AutoMobile &x){return x.getType()==type;}),
cars.end());
Bellow I provide the complete code for something really simple which I'm struggling with..
I need to create a map with strings and Objects...
When requested, if the string is inside the map, I need to return a
reference to one object inside the map
When the string is not inside the map, I need to create that object,
with that string and return (as before) the reference to the new
created object
Please check below the two comments I say "ERROR" to see where the problem is.
My questions are:
How can I insert to a map, one object? what is wrong with the line
on the InitObj()?
How can I create return a reference to an object which I've just
created on a map? As seen at the end of getHouse() function
Thanks in advance :)
#include <map>
#include <string>
#include <memory>
class House
{
public:
House(const char* name) : _name(name) {};
~House() {};
std::string getHouseName () { return _name; }
private:
std::string _name;
House(const House& copy)
{
}
House& operator=(const House& assign)
{
}
};
class Obj
{
public:
Obj()
{
InitObj();
}
~Obj() {};
House& getHouse (const char *houseName)
{
std::string name = houseName;
auto i = _myHouseMap.find(name);
//this string doesn't exist on map? then create a new house and add to the map and return the reference to it
if (i == _myHouseMap.end())
{
//create a new house
House h(houseName);
//add to the map
_myHouseMap.insert(std::pair<const std::string, House>(houseName, h));
//return the reference to the house created
return h; //<--- ERROR!!!! need to return the reference!
}
return (i->second);
}
private:
Obj(const Obj& copy);
Obj& operator=(const Obj& assign);
typedef std::map<const std::string, House> myHouseMap;
myHouseMap _myHouseMap;
//the initialization will add one object to my map
void InitObj()
{
House h("apartment");
_myHouseMap.insert(std::pair<const std::string, House>("apartment", h)); //<--- ERROR see reference to function template instantiation 'std::pair<_Ty1,_Ty2>::pair<const char(&)[10],House&>
}
};
int main(void)
{
Obj aaa;
House& myHouse1 = aaa.getHouse ("apartment");
std::cout << "House is " << myHouse1.getHouseName ();
House& myHouse2 = aaa.getHouse ("newHouse"); //here a new house must be created and added to the map
std::cout << "House is " << myHouse2.getHouseName ();
return 0;
}
For your first question, you made House noncopyable (your copy constructor and copy assignment operator are private). The approach you are taking to insert requires you to make a pair first, the construction of which will copy the House you pass in. If you have access to a C++11 compiler, you can still have the value-type of your map be House and just use emplace instead:
void InitObj()
{
_myHouseMap.emplace(std::piecewise_construct,
std::forward_as_tuple("apartment"), //key
std::forward_as_tuple("apartment")); //value
}
If you don't have access to a C++11 compiler, you will have to change the value type to be House* or some equivalent copy-constructible type.
For the second question, std::map::insert (and emplace) return a pair<iterator, bool>. Just take advantage of that:
if (i == _myHouseMap.end())
{
House h(houseName);
// we insert our new house into the map
// insert() will return a pair<iterator, bool>.
// the bool will be true if the insert succeeded - which we know
// it will because we know that this key isn't already in the map
// so we just reassign 'i' to be insert().first, the new iterator
// pointing to the newly inserted element
i = _myHouseMap.insert(std::pair<const std::string, House>(houseName, h)).first;
}
// here i either points to the element that was already in the map
// or the new element that we just inserted. either way,
// we want the same thing
return i->second;
I have a function that returns an iterator if an object is found.
Now i have a problem. How do i fix the problem of informing the object that called this function that the object was not found?
vector<obj>::iterator Find(int id, int test)
{
vector<obj>::iterator it;
aClass class;
for(it = class.vecCont.begin(); it != class.vecCont.end(); ++it)
{
if(found object) //currently in psuedo code
return it;
}
return ???? // <<< if not found what to insert here?
}
Do i need to change my data structure in this instead?
Thanks in advance! :)
Return vector::end(), throw an exception, or return something other than a plain iterator
Better yet, don't implement your own Find function. That is what the <algorithm> library is for. Based on your psudocode, you can probably use std::find or std::find_if. find_if is particularly useful in cases where equality doesn't necessarily mean operator==. In those cases, you can use a [C++11] lambda or if C++11 isn't available to you, a functor class.
Since the functor is the lowest common denominator, I'll start with that:
#include <cstdlib>
#include <string>
#include <algorithm>
#include <vector>
#include <functional>
using namespace std;
class Person
{
public:
Person(const string& name, unsigned age) : name_(name), age_(age) {};
string name_;
unsigned age_;
};
class match_name : public unary_function <bool, string>
{
public:
match_name(const string& rhs) : name_(rhs) {};
bool operator()(const Person& rhs) const
{
return rhs.name_ == name_;
}
private:
string name_;
};
#include <iostream>
int main()
{
vector<Person> people;
people.push_back(Person("Hellen Keller", 99));
people.push_back(Person("John Doe", 42));
/** C++03 **/
vector<Person>::const_iterator found_person = std::find_if( people.begin(), people.end(), match_name("John Doe"));
if( found_person == people.end() )
cout << "Not FOund";
else
cout << found_person->name_ << " is " << found_person->age_;
}
found_person now points to the person whose name is "John Doe", or else points to people_.end() if that person wasn't found.
A C++11 lambda is new language syntax that makes this process of declaring/defining a functor and using is somewhat simpler for many cases. It's done like this:
string target = "John Doe";
vector<Person>::const_iterator found_person = std::find_if(people.begin(), people.end(), [&target](const Person& test) { return it->name_ == target; });
You can return an iterator to the end, i.e. return class.vecCont.end() to indicate that.
How about just returning the end iterator?
Your code becomes:-
vector<obj>::iterator Find(int id, int test)
{
vector<obj>::iterator it;
aClass class;
for(it = class.vecCont.begin(); it != class.vecCont.end(); ++it)
{
if(found object) //currently in psuedo code
break;
}
return it;
}
or just use std::find.
You should return class.vecCont.end() if the object was not found. But #chris is right - this is exactly what std::find is for.
Something like this
std::vector<obj>::iterator pos;
pos = find(coll.begin(),coll.end(), val);
And don't forget to these check for presence of your element or not in the container
if (pos != coll.end())
Don't return an iterator to a hidden container. Return simply what it is that you want, namely a means to access an object if it exists. In this example, I store the objects in the container via pointer. If your objects only exist temporarily, then new one up and copy the object over!
class AClass;
//...some time later
std::vector<AClass*> vecCont; //notice, store pointers in this example!
//..some time later
AClass * findAClass(int id, int test)
{
vector<AClass*>::iterator it;
for(it = class.vecCont.begin(); it != class.vecCont.end(); ++it)
{
if(found object) //currently in psuedo code
return it;
}
return NULL;
}
//later still..
AClass *foundVal = findAClass(1, 0);
if(foundVal)
{
//we found it!
}
else
{
//we didn't find it
}
edit: the intelligent thing to do is to write a comparator for your class and use the std algorithms sort and to find them for you. However, do what you want.
Never emulate std::algorithm functions inside a class. They are free functions for a reason. It usually is enough to expose begin and end member function that return the right iterators (and possibly a boost::iterator_range). If you need to do a fancy find with a functor, expose the functor as well.