Is there any practical way to get objects to work with maps? (I don't really know much about maps so sorry if this is a bad question). I'm interested in using a map for an inventory system that will contain item objects. An item object has a name, description, and money value. The key would be the item's name, and the linking variable would be the the quantity of items I have for that particular item.
And if a map doesn't work, does anyone have a good alternative to this type of system? I need something keeping track of the quantity of each type of item I have.
The C++ standard library template map is just a storage container so it can definitely be used with objects. The map will take your object as its templated argument parameter.
A map would work well for your inventory system. Use something like:
#include <pair>
#include <map>
#include <string>
#include <iostream>
class Item {
public:
Item(void) {}
~Item(void) {}
Item(std::string new_name) {
my_name=new_name;
}
void setName(std::string new_name) {
my_name= new_name;
}
std::string getName(void) {
return my_name;
}
private:
std::string my_name;
};
class Item_Manager {
public:
Item_Manager(void) {}
~Item_Manager(void) {}
void addItem(Item * my_item, int num_items) {
my_item_counts.insert( std::pair<std::string,int>(Item.getName(),num_items) );
}
int getNumItems(std::string my_item_name) {
return my_item_counters[my_item_name];
}
private:
std::map<std::string, int> my_item_counts;
};
main () {
Item * test_item = new Item("chips");
Item * test_item2 = new Item("gum");
Item_Manager * my_manager = new Item_Manager();
my_manager->addItem(test_item, 5);
my_manager->addItem(test_item2,10);
std::cout << "I have " << my_manager->getNumItems(test_item->getName())
<< " " << test_item->getName() << " and "
<< my_manager->getNumItems(test_item2->getName())
<< " " << test_item2->getName() << std::endl;
delete test_item;
delete test_item2;
delete my_manager;
}
Here's a reference on the stdlib map and its functions:
http://www.cplusplus.com/reference/stl/map/
Look at the function pages for examples of how to iterate through/index a map, etc.
If you're talking about std::map, it's a template which can work with any type of object, as long as a way to compare objects for ordering is provided. Since your key (the name) is a string, it will work right out of the box with std::map
struct Item
{
std::string description;
int value;
};
int main()
{
// associate item names with an item/quantity pair
std::map<std::string, std::pair<Item, int> > itemmap;
}
I need something keeping track of the quantity of each type of item I have.
How about std::vector<std::pair<Item, int> >?
Related
Im just starting to learn C++ and i would like to acess and modify a vector of objects that have different variable types. I have only been able to create the vector, but cant do anything with it. How can i acess and modify it?
This is the code i have created so far:
class Person {
private: string name; int id;
public:
Person(string x, int y) { name = x; id = y; };
~Person();
};
int main()
{
vector<vector<Person>> v4;
}
To access objects stored in the vector you have different operators and methods such as at() or operator[] For more details check https://www.cplusplus.com/reference/vector/vector/
Since you want to learn C++, I assume you do not know what exactly to ask, so let me help you
Your code example lacks further things, besides accessing the objects in the vector:
Since name and id are private you cannot access them.
Your destructor has no implementation
You created a vector of vectors of Persons. I assume you want just a vector.
Your question regarding multiple types in one vector: This is not easily possible. Depending on what you want, you need multiple vectors, or need to combine your data (e.g. through std::pair, or you need to work with interfaces and inheritance.
Your vector is empty
Let me construct something to give you a hint on where to continue:
#include <iostream>
#include <string>
#include <vector>
class Person {
private:
std::string name;
int id;
public:
Person(std::string x, int y) { name = x; id = y; };
// ~Person(); //This is not needed, since your class currently does not need any special handling during destruction
// You need to be able to access name and id to be able to do something with them, for example print it
std::string getName() const {
// be aware that this creates a copy of name. Thus changes to the returned string will not be reflected in the Person object.
// Another option would be returning a reference (std::string&), but that I leave for you to research yourself
return name;
}
int getId() const {
// same as for the name applies
return id;
}
// This allows us to change the id of a Person
void setId(int newId) {
id = newId;
}
};
int main()
{
// Lets create a Person first:
Person p ("John", 1); // Please note that the string literal here will be used to construct an std::string on the fly and then store it in name of p
// Create a second Person:
Person p2 ("Jim", 2);
// Create a vector and add the persons to it
std::vector<Person> v;
v.push_back(p);
v.push_back(p2);
// Print out the contents of the vector
for (auto& pers : v) {
std::cout << "Person " << pers.getName() << " has id " << pers.getId() << std::endl;
}
// Now change the id of the second person in the vector to 5 (note that indexes start at 0)
v.at(1).setId(5);
// Print again
for (auto& pers : v) {
std::cout << "Person " << pers.getName() << " has id " << pers.getId() << std::endl;
}
}
To play around with it: https://godbolt.org/z/8dfEoa
I hope that helps you getting started.
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 am new in c++, I am trying to make a very simple CRUD program.
In this example, a client bought many things in a store, and this store has the information about the things that this client bought. I called it Inventory.
The store, wants to print a Report for every client. In the code below, the main just has one Inventory, just for sample.
The problem is: When I want to print the Report, I have to get the data from the client, but without lose encapsulation. I mean, I want that no class can modify the content of the inventory.
What I am trying to do is convert the map into a vector (I need something to sort the data) and pass this vector (dinamically allocated).
I am allocating this vector at the class Inventory but who is deleting
is the class Report, this is not the correctly way to do things, but I do not know how to pass this information without doing like this.
Anyway, the report class can get the pointer to a Book, and use its set function or point to other Book. Again, I do not know how to do it correctly.
Can someone give me a tip what I have to do in this case ?
Thanks.
Sorry for the long code.
Main:
int main(void)
{
Inventory i;
Report r(i);
i.addBook("Foo Bar I");
i.addBook("Foo Bar II");
r.generateReport();
return 0;
}
Class Report in .h:
class Report
{
private:
Inventory* i;
public:
Report(Inventory& i);
void generateReport();
};
Class Report in cpp:
Report::Report(Inventory& i)
{
this->i = &i;
}
void Report::generateReport()
{
ofstream out ("Report.txt");
out << "Books: " << endl;
vector<pair<int, Book *>> * b = i->getBooks();
for(pair<int, Book *> p : *b)
{
out << p.first << ": " << p.second.getName() << endl;
}
out << endl;
delete b;
out.close();
}
Class Inventory in .h:
class Inventory
{
private:
map<int, Book *> books;
public:
void addBook(int code, const string& name);
vector<pair<int, Book *>> * getBooks();
};
Class Inventory in .cpp:
void Inventory::addBook(int code, const string& name)
{
books.insert(pair<int, Book *>(code, new Book(name)));
}
vector<pair<int, Book *>> * Inventory::getBooks()
{
return new vector<pair<int, Book *>>(books.begin(), books.end());
}
Your inventory class should have this interface:
class Inventory
{
private:
map<int, Book *> books;
public:
using const_iterator = std::map<int, Book*>::const_iterator;
void addBook(int code, const string& name);
const_iterator begin() const { return books.begin(); }
const_iterator end() const { return books.end(); }
};
no reason to create a copy of the map into a vector! that is inefficient and not reason for doing it.
Rewrite the generateReport to work with this interface as follows:
for(const auto &p : *i)
{
out << p.first << ": " << p.second.getName() << endl;
}
out << endl;
With your approach, the trick to transform the map into a vector introductes only an additional dependency: the report has to know about the internals of the inventory.
I'd rather recommend to go the way of the vistitor design pattern: the goal is to separate the algorithm (producing the report, the visitor) from the datastructure you explore (the inventory and its items).
class Item { ...}; // Your object structure
class Book : public Item { ... };
class Inventory { ...};
class Visitor { ... }; // Algorithms that shall be executed on structure
class Report : public Visitor {...};
This could also simplify the implementation of your inventory, as you'd no longer need to foresee different containers for different kind of items (provided they all inherit from some common element base class).
Theres is plenty of litterature on this pattern. I recommend you Design patterns, elements of reusable object oriented software from Gamma & al: its the orginal textbook, and guess what, the demo code shows an inventory with a pricing visitor ;-)
Here a naive on-line example based on your problem, to illustrate how it could work.
I want to store functions with similar signature in a collection to do something like this:
f(vector<Order>& orders, vector<Function>& functions) {
foreach(process_orders in functions) process_orders(orders);
}
I thought of function pointers:
void GiveCoolOrdersToBob(Order);
void GiveStupidOrdersToJohn(Order);
typedef void (*Function)(Order);
vector<Function> functions;
functions.push_back(&GiveStupidOrdersToJohn);
functions.push_back(&GiveCoolOrdersToBob);
Or polymorphic function objects:
struct IOrderFunction {
virtual void operator()(Order) = 0;
}
struct GiveCoolOrdersToBob : IOrderFunction {
...
}
struct GiveStupidOrdersToJohn : IOrderFunction {
...
}
vector<IOrderFunction*> functions;
functions.push_back(new GiveStupidOrdersToJohn());
functions.push_back(new GiveCoolOrdersToBob());
Premise:
The design you propose will work, but using regular function pointers will limit you considerably in the kind of callbacks you can register, and although more powerful, the approach based on inheritance from a fixed interface is more verbose and requires more work for a client to define callbacks.
In this answer I will first show some examples of how to use std::function for this purpose. The examples will pretty much speak for themselves, showing how and why using std::function brings advantages as opposed to the kind of solutions you outlined.
However, a naive approach based on std::function will also have limitations of its own, which I am going to list. This is why I eventually suggest you to have a look at Boost.Signals2: it is a pretty powerful and easy-to-use library. I will address Boost.Signals2 at the end of this answer. Hopefully, understanding a simple design based on std::function first will make it easier for you to grasp the more complex aspects of signals and slots later on.
Solution based on std::function<>
Let's introduce a couple of simple classes and prepare the ground for some concrete examples. Here, an order is something which has an id and contains several items. Each item is described by a type (for simplicity, here it can be either a book a dvd), and a name:
#include <vector>
#include <memory>
#include <string>
struct item // A very simple data structure for modeling order items
{
enum type { book, dvd };
item(type t, std::string const& s) : itemType(t), name(s) { }
type itemType; // The type of the item
std::string name; // The name of the item
};
struct order // An order has an ID and contains a certain number of items
{
order(int id) : id(id) { }
int get_id() const { return id; }
std::vector<item> const& get_items() const { return items; }
void add_item(item::type t, std::string const& n)
{ items.emplace_back(t, n); }
private:
int id;
std::vector<item> items;
};
The heart of the solution I am going to outline is the following class order_repository, and its internal usage of std::function to hold callbacks registered by clients.
Callbacks can be registered through the register_callback() function, and (quite intuitively) unregistered through the unregister_callback() function by providing the cookie returned by registered_callback() upon registration:
The function than has a place_order() function for placing orders, and a process_order() function that triggers the processing of all orders. This will cause all of the registered handlers to be invoked sequentially. Each handler receives a reference to the same vector of placed orders:
#include <functional>
using order_ptr = std::shared_ptr<order>; // Just a useful type alias
class order_repository // Collects orders and registers processing callbacks
{
public:
typedef std::function<void(std::vector<order_ptr>&)> order_callback;
template<typename F>
size_t register_callback(F&& f)
{ return callbacks.push_back(std::forward<F>(f)); }
void place_order(order_ptr o)
{ orders.push_back(o); }
void process_all_orders()
{ for (auto const& cb : callbacks) { cb(orders); } }
private:
std::vector<order_callback> callbacks;
std::vector<order_ptr> orders;
};
The strength of this solution comes from the use of std::function to realize type erasure and allow encapsulating any kind of callable object.
The following helper function, which we will use to generate and place some orders, completes the set up (it simply creates four orders and adds a few items to each order):
void generate_and_place_orders(order_repository& r)
{
order_ptr o = std::make_shared<order>(42);
o->add_item(item::book, "TC++PL, 4th Edition");
r.place_order(o);
o = std::make_shared<order>(1729);
o->add_item(item::book, "TC++PL, 4th Edition");
o->add_item(item::book, "C++ Concurrency in Action");
r.place_order(o);
o = std::make_shared<order>(24);
o->add_item(item::dvd, "2001: A Space Odyssey");
r.place_order(o);
o = std::make_shared<order>(9271);
o->add_item(item::dvd, "The Big Lebowski");
o->add_item(item::book, "C++ Concurrency in Action");
o->add_item(item::book, "TC++PL, 4th Edition");
r.place_order(o);
}
Now let's see what kinds of callback we can provide. For starter, let's have a regular callback function that prints all of the orders:
void print_all_orders(std::vector<order_ptr>& orders)
{
std::cout << "Printing all the orders:\n=========================\n";
for (auto const& o : orders)
{
std::cout << "\torder #" << o->get_id() << ": " << std::endl;
int cnt = 0;
for (auto const& i : o->get_items())
{
std::cout << "\t\titem #" << ++cnt << ": ("
<< ((i.itemType == item::book) ? "book" : "dvd")
<< ", " << "\"" << i.name << "\")\n";
}
}
std::cout << "=========================\n\n";
}
And a simple program that uses it:
int main()
{
order_repository r;
generate_and_place_orders(r);
// Register a regular function as a callback...
r.register_callback(print_all_orders);
// Process the order! (Will invoke all the registered callbacks)
r.process_all_orders();
}
Here is the live example showing the output of this program.
Quite reasonably, you are not limited to registering regular functions only: any callable object can be registered as a callback, including a functor holding some state information. Let's rewrite the above function as a functor which can either print the same detailed list of orders as function print_all_orders() above, or a shorter summary that does not include order items:
struct print_all_orders
{
print_all_orders(bool detailed) : printDetails(detailed) { }
void operator () (std::vector<order_ptr>& orders)
{
std::cout << "Printing all the orders:\n=========================\n";
for (auto const& o : orders)
{
std::cout << "\torder #" << o->get_id();
if (printDetails)
{
std::cout << ": " << std::endl;
int cnt = 0;
for (auto const& i : o->get_items())
{
std::cout << "\t\titem #" << ++cnt << ": ("
<< ((i.itemType == item::book) ? "book" : "dvd")
<< ", " << "\"" << i.name << "\")\n";
}
}
else { std::cout << std::endl; }
}
std::cout << "=========================\n\n";
}
private:
bool printDetails;
};
Here is how this could be used in a small test program:
int main()
{
using namespace std::placeholders;
order_repository r;
generate_and_place_orders(r);
// Register one particular instance of our functor...
r.register_callback(print_all_orders(false));
// Register another instance of the same functor...
r.register_callback(print_all_orders(true));
r.process_all_orders();
}
And here is the corresponding output shown in this live example.
Thanks to the flexibility offered by std::function, we can also register the result of std::bind() as a callback. To demonstrate this with an example, let's introduce a further class person:
#include <iostream>
struct person
{
person(std::string n) : name(n) { }
void receive_order(order_ptr spOrder)
{ std::cout << name << " received order " << spOrder->get_id() << std::endl; }
private:
std::string name;
};
Class person has a member function receive_order(). Invoking receive_order() on a certain person object models the fact that a particular order has been delivered to that person.
We could use the class definition above to register a callback function that dispatches all the orders to one person (which can be determined at run-time!):
void give_all_orders_to(std::vector<order_ptr>& orders, person& p)
{
std::cout << "Dispatching orders:\n=========================\n";
for (auto const& o : orders) { p.receive_order(o); }
orders.clear();
std::cout << "=========================\n\n";
}
At this point we could write the following program, that register two callbacks: the same function for printing orders we have used before, and the above function for dispatching orders to a certain instance of Person. Here is how we do it:
int main()
{
using namespace std::placeholders;
order_repository r;
generate_and_place_orders(r);
person alice("alice");
r.register_callback(print_all_orders);
// Register the result of binding a function's argument...
r.register_callback(std::bind(give_all_orders_to, _1, std::ref(alice)));
r.process_all_orders();
}
The output of this program is shown in this live example.
And of course one could use lambdas as callbacks. The following program builds on the previous ones to demonstrate the usage of a lambda callback that dispatches small orders to one person, and large orders to another person:
int main()
{
order_repository r;
generate_and_place_orders(r);
person alice("alice");
person bob("bob");
r.register_callback(print_all_orders);
r.register_callback([&] (std::vector<order_ptr>& orders)
{
for (auto const& o : orders)
{
if (o->get_items().size() < 2) { bob.receive_order(o); }
else { alice.receive_order(o); }
}
orders.clear();
});
r.process_all_orders();
}
Once again, this live example shows the corresponding output.
Beyond std::function<> (Boost.Signals2)
The above design is relatively simple, quite flexible, and easy to use. However, there are many things it does not allow to do:
it does not allow to easily freeze and resume the dispatching of events to a particular callback;
it does not encapsulate sets of related callbacks into an event
class;
it does not allow grouping callbacks and ordering them;
it does not allow callbacks to return values;
it does not allow combining those return values.
All these feature, together with many others, are provided by full-fledged libraries such as Boost.Signals2, which you may want to have a look at. Being familiar with the above design, it will be easier for you to understand how it works.
For instance, this is how you define a signal and register two simple callbacks, and call them both by invoking the signal's call operator (from the linked documentation page):
struct Hello
{
void operator()() const
{
std::cout << "Hello";
}
};
struct World
{
void operator()() const
{
std::cout << ", World!" << std::endl;
}
};
int main()
{
boost::signals2::signal<void ()> sig;
sig.connect(Hello());
sig.connect(World());
sig();
}
As usual, here is a live example for the above program.
You might want to look into std::function, your vector would then look like this:
std::vector< std::function< void( Order ) > > functions;
But be aware that std::function has a small overhead. For the instances, drop the new:
function.push_back(GiveStupidOrdersToJohn());
Boost.Signal solves exactly your problem. You should have a look into that. Unless you have special requirements. In particular boost.signal and boost.function and/or std::function
use type erasure techniques. Thus, you are able to have a vector of callable things with a specified signature. It does not matter if your entities are plain C-functions (as you have in your example) or function-objects or member-functions in general. You can mix all of them.
I have a class which made of a ints and strings, but I also have a vector inside that class. I have to read the records from a file and then after parsing each line, put the info in my vector of class. I have to get the basic package information like ID and name and then add the services which are offered with that package, so I can have 10 records that are from one package but they are different in type of services. For now I am trying to work out putting the data in each package and access the data from each element, but when I am trying to get the data from the vector inside the class, my compiled file crashes. It also prints out the 1233 and foo, but not the test. Any ideas why is that?
int main()
{
vector<package> packs;
package pack;
pack.ID = 1233;
pack.name = "foo";
packs.push_back(pack);
pack.putData("test",12);
cout << packs[0].name << endl;
cout << packs[0].ID << endl;
cout << packs[0].bservice[0].serviceID << endl; //[b]Crashes in this line[/b]
return 0;
}
Defined class is:
class package
{
public:
class aservice
{
public:
int serviceID;
string othername;
};
int ID;
string name;
vector<aservice> bservice;
void putData(string name1, int serviceID1)
{
aservice obj;
obj.serviceID = serviceID1;
obj.othername = name1;
bservice.push_back(obj);
}
};
Here you make a copy of pack when you push_back into the vector:
packs.push_back(pack);
And here you access pack, not the copy stored in your vector
pack.putData("test",12);
So the bservice vector you are trying to access is actually empty, which is why your code crashes when you try to access it here:
cout << patients[0].bservice[0].serviceID << endl; // patients[0].bservice is empty!!!
You can avoid this by pushing back after the call to putData:
vector<package> packs;
package pack;
pack.ID = 1233;
pack.name = "foo";
pack.putData("test",12);
packs.push_back(pack);
You can also avoid it by not trying to access a vector without first checking whether it is empty.
Ideally you should strive to design classes than can be constructed into a useful state, as opposed to default constructing them and adding data step by step via setters. This is particularly important if the data are inter-related and the class must maintain invariants.
packs.push_back(pack);
Is going to push a copy of pack into your vector. You will therefore have two specific instances : if you call putData on one of those, the other one will not be modified itself !
Therefore, when writing
patients[0].bservice[0]
your application crashes because you did not putData inside patients[0], only inside pack - which is, once again, a different object.
You should modify your vector so it stores pointers to package's, and push the adress of pack inside.
pack.push_back(pack);
Assuming the first pack is actually packs, this pushes a copy of pack onto the vector.
pack.putData("test",12);
This modifies the local variable pack, but not the copy you pushed onto the vector. That still contains an empty bservice vector.
cout << patients[0].bservice[0].serviceID << endl;
Assuming that patients is actually packs, this erroneously attempts to read from the empty bservice vector.
You either want to call putData before packs.push_back(pack), or call it on packs.back() rather than the local pack.
Try this:
#include <vector>
#include <iostream>
using namespace std;
class package
{
public:
package(int inID, const string& inName ) : ID(inID), name(inName)
{
}
void putData(string name1, int serviceID1)
{
aservice obj;
obj.serviceID = serviceID1;
obj.othername = name1;
bservice.push_back(obj);
}
void Print() const
{
cout << ID << endl;
cout << name << endl;
vector<aservice>::const_iterator iter;
iter = bservice.begin();
for (; iter != bservice.end(); ++iter)
{
cout << iter->serviceID << " " << iter->othername << endl;
}
}
private:
class aservice
{
public:
aservice() {};
int serviceID;
string othername;
};
int ID;
string name;
vector<aservice> bservice;
};
typedef vector<package> PackContainer;
typedef vector<package>::iterator PackContainerIterator;
typedef vector<package>::const_iterator PackContainerConstIterator;
void PrintAll(const PackContainer& packs)
{
PackContainerConstIterator iter = packs.begin();
for (; iter != packs.end(); ++iter)
{
iter->Print();
}
}
int main()
{
PackContainer packs;
package pack( 1233, "foo");
pack.putData("test",12);
packs.push_back(pack);
PrintAll(packs);
return 0;
}