How to insert to std::map in C++11? - c++

I am trying to insert a set of pair values into a std::map in c++11. However, the values don't seem to insert into the std::map. Please do go over my code about the same. I appreciate any and all help.
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<cstdlib>
#include<utility>
#include<ctime>
#include "print.h"
class ReportCard
{
private:
std::map<std::string, double> m_report_card;
public:
std::map<std::string, double> getReportCardInstance() { return m_report_card; }
};
class Student
{
private:
int m_roll_no;
std::string m_name;
ReportCard m_reportCard;
public:
Student(int inRollNo, const std::string& inName) :
m_roll_no(inRollNo), m_name(inName)
{}
std::string getName() { return m_name; }
int getRollNo() { return m_roll_no; }
ReportCard getReportCard() { return self.m_reportCard; }
int getReportCardSize() { return m_reportCard.getReportCardInstance().size(); }
};
class Driver
{
private:
std::vector<Student> student_list;
std::vector<Student> temp;
public:
void studentTestPopulate()
{
student_list.push_back(Student(1, "Tim"));
student_list.push_back(Student(2, "Matt"));
student_list.push_back(Student(100, "Luke"));
student_list.push_back(Student(68, "Lissy"));
student_list.push_back(Student(20, "Tony"));
student_list.push_back(Student(33, "Joseph"));
student_list.push_back(Student(14, "Sid"));
student_list.push_back(Student(15, "Roby"));
student_list.push_back(Student(44, "Rohan"));
student_list.push_back(Student(11, "Kevin"));
student_list.push_back(Student(19, "George"));
}
void reportCardPopulate()
{
for (auto& student : student_list)
{
std::cout << student.getName() << std::endl;
student.getReportCard().getReportCardInstance().insert(std::make_pair<std::string, double>("Math", generateMark));
//This is the function that does not work. No marks are printed!!
for (auto& mark : student.getReportCard().getReportCardInstance())
{
std::cout << mark.first << " " << mark.second;
}
//student.getReportCard().getReportCardInstance().insert(std::make_pair("Science", generateMark));
//student.getReportCard().getReportCardInstance().insert(std::make_pair("Geography", generateMark));
//student.getReportCard().getReportCardInstance().insert(std::make_pair("French", generateMark));
//student.getReportCard().getReportCardInstance().insert(std::make_pair("History", generateMark));
}
}
void showAllStudentDetails()
{
for (auto& student : student_list)
{
std::cout << student.getName() << std::endl;
std::cout << student.getRollNo() << std::endl;
std::cout << "REPORT CARD : " << student.getReportCardSize() << std::endl << std::endl;
for (auto& mark : student.getReportCard().getReportCardInstance())
{
std::cout << mark.first << std::endl;
std::cout << mark.second << std::endl;
}
}
}
};
int main()
{
srand(time(NULL));
Driver driver;
driver.studentTestPopulate();
driver.reportCardPopulate();
//driver.showAllStudentDetails();
}
The reportCardPopulate() function is supposed to insert pairs of values into a report_card map. However, the insert function doesn't seem to work.
When we try to print the values within the reportCardPopulate() function, it doesn't print anything. When I try to print the size of the map, it prints 0. When I printed the size using sizeof() it prints the same size before and after the insertion.

The following functions
std::map<std::string, double> getReportCardInstance() { ... }
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ReportCard getReportCard() { ... }
//^^^^^^^^
returns the copy of std::map<std::string, double> and ReportCard class respectively. Therefore, whatever you insert here
student.getReportCard().getReportCardInstance().insert(std::make_pair<std::string, double>("Math", generateMark));
does on the copies of the above, hence the original member in ReportCard(i.e. m_report_card) will never get be updated. After the call of above line, the copies will be destroyed and expecting it to work make no sense.
Secondly, shown code is wrong, because in c++ you should have used this not self
ReportCard getReportCard()
{
return self.m_reportCard;
//^^^^ --> should be `return this->m_reportCard;`
// or simply `return m_reportCard;`
}
Correcting the above, and returning the member by reference will make the code work.
(See live online)
std::map<std::string, double>& getReportCardInstance()
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
return m_report_card;
}
ReportCard& getReportCard()
//^^^^^^^^
{
return m_reportCard;
}
That being said, your ReportCard and Student classes will expose the members, if you do the above. Which is not a good design. If those are meant only for the internal uses of Driver class, you could keep them as private properties of Driver class.
#include <vector>
#include <map>
#include <string>
#include <iostream>
class Driver /* final */ // -> optional
{
private: // Student is private for Driver class
class Student
{
// type alias is enough for the map
using ReportCard = std::map<std::string, double>;
private:
int m_roll_no;
std::string m_name;
ReportCard m_reportCard;
public:
Student(int inRollNo, const std::string& inName)
: m_roll_no{ inRollNo }, m_name{ inName }
{}
// make the member functions const if they are not modifing the members
const std::string& getName() const { return m_name; }
int getRollNo() const { return m_roll_no; }
ReportCard& getReportCard() { return m_reportCard; }
std::size_t getReportCardSize() const { return m_reportCard.size(); }
};
private:
std::vector<Student> student_list;
std::vector<Student> temp;
public:
void studentTestPopulate()
{
// construct the `Student` in-place using `std::vector::emplace_back`
student_list.emplace_back(1, "Tim");
student_list.emplace_back(2, "Matt");
student_list.emplace_back(100, "Luke");
student_list.emplace_back(68, "Lissy");
student_list.emplace_back(20, "Tony");
student_list.emplace_back(33, "Joseph");
student_list.emplace_back(14, "Sid");
student_list.emplace_back(15, "Roby");
student_list.emplace_back(44, "Rohan");
student_list.emplace_back(11, "Kevin");
student_list.emplace_back(19, "George");
}
void reportCardPopulate()
{
for (auto& student : student_list)
{
std::cout << student.getName() << "\n";
student.getReportCard().emplace(student.getName(), 12.0);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// use `std::map::emplace` for constructing `ReportCard` in-place
for (auto& mark : student.getReportCard())
{
std::cout << mark.first << " " << mark.second << "\n";
}
}
}
// ... other members
};
int main()
{
Driver driver;
driver.studentTestPopulate();
driver.reportCardPopulate();
}

#include <iostream>
#include <map>
class ReportCard
{
//private: this is the default anyway for a class
public: //made to be able to print the internals below.
std::map<std::string, double> m_report_card;
public:
/* this returns an instance of the std::map. The map is copied and
returned, so any modifications will not affect m_report_card
std::map<std::string, double> getReportCardInstance()
{
return m_report_card;
}
if you want to do this, return std::map<std::string, double>&.
std::map<std::string, double>& getReportCardInstance()
{
return m_report_card;
}
*/
// better solution is to have a method to add the report
void add_report(const std::string& first,double second)
{
m_report_card[first] = second;
}
};
int main() {
ReportCard rc;
rc.add_report("Percy",1.0);
rc.add_report("Pig",2.0);
for(auto internal_report_card : rc.m_report_card)
{
std::cout << internal_report_card.first << ", "
<< internal_report_card.second << std::endl;
}
return 0;
}
Demo

Related

C++ vector of struct allocated on stack

If we have a vector of struct pointer MyInfo* (allocated on heap). Then we can check vec[i] == NULL to know whether there is a struct in the vec[i], like this, if (vec[i] != NULL) //then do some processing
However, if we allocate MyInfo on stack instead of on heap, then we have vector<MyInfo> as shown below. I guess each vec[i] is initialized by the struct default constructor. How do you check whether vec[i] contains a non-empty struct similar to above NULL pointer case, like if (vec[i] contains valid struct) //then do some processing
My code is below
#include <iostream> // std::cout
#include <string>
#include <vector>
using namespace std;
struct MyInfo {
string name;
int age;
};
int main () {
vector<MyInfo> vec(5);
cout << "vec.size(): " << vec.size() << endl;
auto x = vec[0];
cout << x.name << endl; //this print "" empty string
cout << x.age << endl; //this print 0
return 0;
}
There are some options you can use. The first and easiest one, is to define a value to each (or for one) of your struct's variables, that will point that the struct is not initialized yet. In this case, age should be large or equal to 0, to be logicly straight. So, you can initialize it to -1, like this:
struct MyInfo {
string name;
int age = -1;
};
// Or
struct MyInfo {
string name;
int age;
MyInfo() : name(""), age(-1) {} // Use constructor
};
Now, in your main function, it will print in the age the value -1. Also, you can see the empty of the name variable as a sign for it too.
Another way might be using flag and get/set operations to indicate when the variables are initialize:
struct MyInfo {
private:
std::string _name;
int _age;
bool age_initialize = false;
bool name_initialize = false;
public:
void name(const std::string &name_p) { _name = name_p; name_initialize = true; }
void age(int age_p) { _age = age_p; age_initialize = true; }
void init(int age_p, const std::string &name_p) { age(age_p); name(name_p); }
bool is_initialize() { return name_initialize && age_initialize; }
int age() { return _age; }
std::string name() { return _name; }
};
int main() {
std::vector<MyInfo> vec(5);
std::cout << "vec.size(): " << vec.size() << std::endl;
auto x = vec[0];
std::cout << x.is_initialize() << std::endl; //this print 0
std::cout << x.name() << std::endl; //this print "" empty string
std::cout << x.age() << std::endl; //this print 0
return 0;
}
You can also throw an exception when calling int age() of std::string name() function, if those values are not initialize yet:
struct MyInfo {
private:
/* ... */
public:
/* ... */
int age() {
if (!age_initialize) throw std::runtime_error("Please initialize age first.");
return _age;
}
std::string name() {
if (!name_initialize) throw std::runtime_error("Please initialize name first.");
return _name;
}
};

Make a collection of an abstract class type, Abstract Class vector of shared_ptr

Error
e/c++/v1/algorithm:642:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/utility:321:9: error:
field type 'Space' is an abstract class
_T2 second;
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/map:624:16: note:
Question
How can I define a std::vector of type Space which is an abstract class and then fill this vector with instances of the derived classes Empty, Snake, Ladder.
Context
I know abstract classes in C++ can not be instantiated. Instead I've read in several posts on this and other sites that you can create a collection of an abstract type if it the type is defined as a star * pointer or any of the <memory> managed pointer data types like std::unqiue_ptr<T>. I've tried to used shared_ptr<Space> in my case, but still unable to define the collection properly. I am compiled my code using g++ -std=c++17 main.cpp && ./a.out.
Code
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <map>
#include <memory>
#include <typeinfo>
#include <queue>
#include <string>
#include <vector>
class Player
{
private:
int m_current_space = 1;
public:
Player() {}
void role_dice() {
m_current_space += floor( (rand()%10 + 1) / 3 );
}
int const get_current_space() {
return m_current_space;
}
void set_current_space(int current_space) {
m_current_space = current_space;
}
};
class Space
{
protected:
int m_id;
std::vector<Space> m_paths;
public:
Space() {} // requied to use [] operator in map
Space(int id) : m_id(id) {}
void add_path(Space& s) {
m_paths.push_back(s);
}
int get_id() {
return m_id;
}
virtual std::string class_type() = 0;
};
class Empty : public Space
{
public:
Empty(int id) : Space(id) {}
std::string class_type() {
return "Empty";
}
};
class Ladder : public Space
{
public:
Ladder(int id) : Space(id) {}
virtual void event(Player& p) {
p.set_current_space(1);
}
std::string class_type() {
return "Ladder";
}
};
class Snake : public Space
{
public:
Snake(int id) : Space(id) {}
virtual void event(Player& p) {
p.set_current_space(4);
}
std::string class_type() {
return "Snake";
}
};
class Board
{
private:
std::map<int, Space> m_board;
public:
void add_space(Space& s) {
m_board[s.get_id()] = s;
}
void draw_board() {
int i = 1;
for(auto const& [space_key, space] : m_board) {
if(i%3 == 0) {
std::cout << "○\n";
}
else if(typeid(space) == typeid(Snake)) {
std::cout << "○-";
}
else {
std::cout << "○ ";
}
++i;
}
}
void update_player_on_board(int position) {
int i = 1;
for(auto const& [space_key, space] : m_board) {
if(i%3 == 0) {
if (space_key == position) {
std::cout << "●\n";
}
else {
std::cout << "○\n";
}
}
else if(typeid(space) == typeid(Snake)) {
std::cout << "○-";
}
else {
if (space_key == position) {
std::cout << "● ";
}
else {
std::cout << "○ ";
}
}
++i;
}
}
const std::map<int, Space> get_board() {
return m_board;
}
friend std::ostream &operator<<(std::ostream& os, const Board& b) {
return os;
}
};
class GameStateManager
{
private:
std::string m_state = "game over";
bool m_playing = false;
public:
std::string const get_state() {
return m_state;
}
void set_state(std::string state) {
m_state = state;
}
};
int main()
{
std::cout << "Welcome to Bowser's 9 board game\n";
std::cout << "Start? y(yes) n(no)\n";
GameStateManager game_manager;
game_manager.set_state("playing");
auto space1 = std::make_shared<Space>(1);
auto space2 = std::make_shared<Space>(2);
auto space3 = std::make_shared<Space>(3);
auto space4 = std::make_shared<Space>(4);
auto space5 = std::make_shared<Space>(5);
auto space6 = std::make_shared<Space>(6);
auto space7 = std::make_shared<Space>(7);
auto space8 = std::make_shared<Space>(8);
auto space9 = std::make_shared<Space>(9);
std::vector<std::shared_ptr<Space>> v {
space1, space2, space3,
space4, space5, space6,
space7, space8, space9
};
Board bowsers_bigbad_laddersnake;
for(int i = 0; i < 10; ++i) {
bowsers_bigbad_laddersnake.add_space(*(v[i]));
}
bowsers_bigbad_laddersnake.draw_board();
Player mario;
int turn = 0;
while(game_manager.get_state() == "playing") {
std::cin.get();
std::cout << "-- Turn " << ++turn << " --" << '\n';
mario.role_dice();
bowsers_bigbad_laddersnake.update_player_on_board(mario.get_current_space());
if (mario.get_current_space() >= 9) {
game_manager.set_state("game over");
}
}
std::cout << "Thanks a so much for to playing!\nPress any key to continue . . .\n";
std::cin.get();
return 0;
}
You seem to have removed a lot of code to get into details here.
Have a Space pointer (smart or raw). Instantiate the specific space that you want, point to it with your pointer of type Space. Example std::shared_ptr<Space> pointerToSpace = std::make_shared<Snake> ("I'm a snake"); Now, without loss of generality, you can print the contents (of concrete type) with just the pointer to the space pointerToSpace->class_type(). Yes, you can have a collection of shared_ptrs in a container.

c++ How can I access the Vector Data created by Composition?

I'll go straight to an example, I think it is easier to underestand.
Music Cd has Tracks. How can I access A TrackInfo vector (XTrackInfo) data "inside" Music Cd class?
I want to print and even change values, I don't figure out how.
Thanks
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <numeric>
class XTrackInfo
{
std::string m_TrackName;
int m_Length;
public:
XTrackInfo() {}
XTrackInfo(std::string TrackName, int Length):
m_TrackName(std::move(TrackName)),
m_Length(Length)
{}
void SetTrackName(std::string TrackName) { m_TrackName = std::move(TrackName); }
void SetTrackLength(int Length) { m_Length = Length; }
const std::string& GetTrackName() const { return m_TrackName; }
int GetTrackLength() const { return m_Length; }
};
class XMusicCd
{
private:
std::string m_Author;
std::vector<XTrackInfo> m_TrackInfo;
public:
XMusicCd() {}
XMusicCd(std::string Author, std::vector<XTrackInfo> Tracks):
m_Author(std::move(Author)),
m_TrackInfo(std::move(Tracks))
{}
void SetAuthor(std::string Author) { m_Author = std::move(Author); }
const std::string& GetAuthor() const { return m_Author; }
const std::vector<XTrackInfo> GetTracks() const { return m_TrackInfo;}
int GetLength() const; // Left incomplete on purpose; you will implement it later
void AddTrack(XTrackInfo NewTrack){
m_TrackInfo.emplace_back(std::move(NewTrack));
}
};
void PrintCdContents(const XMusicCd& Cd)
{
std::cout << "Author : " << Cd.GetAuthor() << "\n";
std::cout << "\n" << std::endl;
std::cout << "Track Info" << std::endl;
//problems here :)
}
int main()
{
// You may not change this function
XMusicCd MyCd;
MyCd.SetAuthor("Hello World");
MyCd.AddTrack(XTrackInfo("This is a test", 100));
MyCd.AddTrack(XTrackInfo("This is a test 2", 200));
PrintCdContents(MyCd);
}
Use iterators:
std::vector<XTrackInfo> tracks = Cd.GetTracks();
for (std::vector<XTrackInfo>::const_iterator it = tracks.begin(); it != tracks.end(); ++it) {
std::cout << it->GetTrackName() << std::endl;
}
Or indexes:
std::vector<XTrackInfo> tracks = Cd.GetTracks();
for (unsigned i = 0; i < tracks.size(); ++i) {
std::cout << tracks.at(i).GetTrackName() << std::endl;
}

Using static maps within a class inside different functions:

I have a class like this:
class SelectorFactory
{
public:
static std::map<std::string,int> _creator;
static void registerCreator(std::string& name,int value)
{
//static std::map<std::string,int> _creator;
if(_creator.end() != _creator.find(name))
{
std::cout << "Selector already registered \n";
}
else
{
std::cout << "Entering " <<name<<" in register: \n";
_creator[name]=value;
}
}
static int createSelector(std::string selectorName)
{
//static std::map<std::string,int> _creator;
std::map< std::string , int >::iterator mapIter=_creator.find(selectorName);
if(mapIter==_creator.end())
{
std::cout<<selectorName<<" Not found in the Map \n" ;
return 0;
}
else
{
int selector= mapIter->second;
return selector;
}
}
};
If I uncomment the commented lines above, code is getting compiled but it's not returning any value from createSelector function which is quite obvious.But if I keep them commented, I am getting error as "_creator was not declared in this scope" inside both the functions.
What should I do to rectify this issue.
In order to have -creator instantiated, you must provide a definition for it. Currently, you have only a declaration.
class SelectorFactory
{
static std::map<std::string,Int> _creator;
};
std::map<std::string,Int> SelectorFactory::_creator;
SelectorFactory.h :
#ifndef __SELECTOR_FACTORY__H__
#define __SELECTOR_FACTORY__H__
#include <string>
#include <map>
class SelectorFactory
{
public:
static void registerCreator(std::string& name,int value);
static int createSelector(std::string selectorName);
private: // !!!!!!!!! NOT PUBLIC!!! >:(
static std::map<std::string,int> _creator;
};
#endif // __SELECTOR_FACTORY__H__
SelectorFactory.cpp :
#include "SelectorFactory.h"
#include <iostream>
std::map<std::string,int> SelectorFactory::_creator;
void SelectorFactory::registerCreator(std::string& name,int value)
{
if(_creator.end() != _creator.find(name))
{
std::cout << "Selector already registered \n";
}
else
{
std::cout << "Entering " <<name<<" in register: \n";
_creator[name]=value;
}
}
int SelectorFactory::createSelector(std::string selectorName)
{
std::map< std::string , int >::iterator mapIter=_creator.find(selectorName);
if(mapIter==_creator.end())
{
std::cout<<selectorName<<" Not found in the Map \n" ;
return 0;
}
else
{
int selector= mapIter->second;
return selector;
}
}

Free memory after remove_if

In the following example I remove from list some elements in the range for which the application of pr2 to it return true.
m_list.remove_if(pr2(*tmp_list));
It seems to me it is necessary to delete this objects, which was removed above, becase when I create it I use "new" (new CRectangle()). How I can do this? I don't know which (and how much) elements will be remove after remove_if.
// test_cconnection.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <conio.h>
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
class CDrawObject
{
public:
virtual ~CDrawObject()
{
cout << "Drop CDrawObject: " << id_ << endl;
}
int getId() const
{
return id_;
}
virtual void draw()
{
}
protected:
static int id;
int id_;
};
class CRectangle : public CDrawObject
{
public:
CRectangle()
{
id_ = id++;
}
~CRectangle()
{
cout << "Drop CRectangle: " << id_ << endl;
}
virtual void draw()
{
cout << "CRectangle, id: " << id_ << endl;
}
};
class CMarker : public CDrawObject
{
CDrawObject* obj;
public:
CMarker(CDrawObject* obj_)
{
obj = obj_;
}
~CMarker()
{
cout << "Delete marker of object with id: " << obj->getId() << endl;
}
CDrawObject* getObject() const
{
return obj;
}
virtual void draw()
{
cout << "CMarker of oject with id: " << obj->getId() << endl;
}
};
int CDrawObject::id = 0;
// predicate for compare objects with int id
class pr : public std::unary_function<CDrawObject*, bool>
{
private:
int id_;
public:
pr(int id): id_(id) {}
bool operator()(CDrawObject* arg) const
{
return (arg->getId() == id_);
}
};
// predicate for check objects with type CMarker and
// compare with CDrawObject* obj
class pr2 : public std::unary_function<CDrawObject*, bool>
{
private:
CDrawObject* obj_;
public:
pr2(CDrawObject* obj)
{
obj_ = obj;
}
bool operator()(CDrawObject* arg) const
{
if (dynamic_cast<CMarker*>(arg))
return ((dynamic_cast<CMarker*>(arg))->getObject() == obj_);
}
};
int _tmain(int argc, _TCHAR* argv[])
{
list<CDrawObject*> m_list;
list<CDrawObject*>::iterator i_list, tmp_list;
m_list.push_back(new CRectangle());
tmp_list = m_list.end();
m_list.push_back(new CMarker(*--tmp_list));
m_list.push_back(new CMarker(*tmp_list));
m_list.push_back(new CRectangle());
tmp_list = m_list.end();
m_list.push_back(new CMarker(*--tmp_list));
m_list.push_back(new CRectangle());
tmp_list = m_list.end();
m_list.push_back(new CMarker(*--tmp_list));
m_list.push_back(new CMarker(*tmp_list));
// print on screen items of m_list
for (i_list = m_list.begin(); i_list != m_list.end(); ++i_list)
(*i_list)->draw();
// get an iterator to the first element in the range with id_ = 2
tmp_list = find_if(m_list.begin(), m_list.end(), pr(2));
if (tmp_list != m_list.end())
{
// remove from list all elements with type CMarker
// and CDrawObject = tmp_list
m_list.remove_if(pr2(*tmp_list));
}
cout << endl << "--------" << endl;
// print on screen items of m_list
for (i_list = m_list.begin(); i_list != m_list.end(); ++i_list)
(*i_list)->draw();
_getch();
return 0;
}
Well you could:
HACKISH: delete the object in the predicate.
ANNOYING: Stay away from remove_if and implement everything it does on your own except add the delete.
BETTER: use RAII objects rather than raw pointers. Some sort of smart ptr in other words.
The way it's implemented at the moment, you won't be able to delete the memory that you allocated for those objects. In general, it takes some extra effort to perform memory cleanup when you have containers of pointers to dynamically allocated memory. Here's one way to do it:
// Assume there's a predicate function called ShouldRemove(int value);
list<int> my_list;
// initialization...
for (list<int>::iterator itr = my_list.begin(); itr != my_list.end(); ) {
if (ShouldRemove(**itr)) {
delete *itr;
itr = my_list.erase(itr);
} else {
++itr;
}
}
But as Noah Roberts pointed out, this is all much easier to deal with if you store your pointers as smart pointers that clean up after themselves.
Standalone remove_if never resizes a collection and returns an iterator pointing to the first object for which predicate is false.
It is therefore more appropriate for your task.