Absolute newbie to c++ (and oop) as well.
Just wanted to ask how to return an object from a list (if it exists), by passing a single id to a getter.
My code is as follows:
class Customer
{
private:
unsigned int custNo;
std::string name;
std::string address;
/* .. */
}
class Store
{
private:
std::string storeName;
std::list<Customer *> customerBase;
std::list<Product *> productStock;
std::list<Sale*> sales;
public:
Store(std::string storeName); // constructor
std::string getStoreName();
Customer & getCustomer(unsigned int custId); //METHOD IN QUESTION
/*..*/
// Constructor
Customer::Customer(std::string name, std::string address)
{
//ctor
}
//
Customer & Store::getCustomer(unsigned int custId){
}
I know this might be a farily basic question. Still I would very much appreciate the help. Thanks in advance!
Just wanted to ask how to return an object from a list (if it exists), by passing a single id to a getter.
Pointer is the first thing that you should think when you see "if it exists". This is because the only representation of an object in C++ that can be optional is a pointer. Values and references must always be present. Therefore, the return type of your function should be Customer*, not Customer&:
Customer* Store::getCustomer(unsigned int custId){
...
}
If you need fast retrieval by an id, use a map<int,Customer*> or unordered_map<int,Customer*>. You could do it with a list, too, but the search would be linear (i.e. you would go through the entire list in the worst case).
Speaking of pointers, if you must store pointers to Customer objects, assuming that the objects themselves are stored in some other container, you may be better off using shared_ptr<Customer> in both containers, to simplify resource management.
You can do this but it would be cumbersome as list is not sorted so you have to traverse the list and check each structure for matching id.
Rather you could store these in std::map with ids as their keys...OR much better unordered_map if you really care about performance.
Assuming you have getCustId() public member function in class Customer:
Customer & Store::getCustomer(unsigned int custId){
auto custIt = find_if(customerBase.begin(), customerBase.end(),
[custId](const Customer& c){ return c.getCustId() == custId });
return *custIt;
}
Related
I am attempting to use std::unordered_set as a hash table to store many CreditCard's. CreditCard and another class CardDatabase are defined as follows:
class CreditCard {
private:
string cardHolder;
unsigned long long cardNumber;
int limit;
int balance;
public:
CreditCard(string in_cardHolder, string in_cardNumber, int in_limit) {
cardHolder = in_cardHolder;
cardNumber = stoll(in_cardNumber);
limit = in_limit;
balance = 0;
}
void ChangeBalance(int amount) const {
balance += amount; // SECOND ERROR
}
};
class CardDatabase {
private:
unordered_set<CreditCard> cards;
unordered_set<CreditCard>::iterator iter;
public:
CardDatabase() { }
void AddCard(cardHolder, cardNumber, int limit) {
CreditCard tempCard = CreditCard(cardHolder, cardNumber, limit);
cards.insert(tempCard);
}
void Charge(string cardHolder, int chargeAmount) {
iter = cards.find(cardHolder);
iter->ChangeBalance(chargeAmount); // FIRST ERROR
}
}
Initially I was getting the following compile error at FIRST ERROR: Member function 'ChangeBalance' not viable: 'this' argument has type 'const CreditCard', but function is not marked const. So, I added the "const" to the ChangeBalance function. However, after doing that I get the following compile error at SECOND ERROR: Cannot assign to non-static member within const member function 'ChangeBalance'.
Is there any way to fix this error without changing balance to a static variable? It is obviously important that the balance be different for each CreditCard instance.
Any help is appreciated.
EDIT:
Thank you all for your quick answers. I feel I should clarify something. I already added the proper hash functionality elsewhere in my code:
namespace std {
template <>
struct hash<CreditCard> {
size_t operator()(const CreditCard& cc) const
{
return hash<string>()(cc.GetCardHolder());
}
}
}
Also, the code I posted initially pasted is from a much larger code base and I didn't delete all the necessary namespacing stuff at first before posting the question. My apologies for the confusion.
Members of an unordered_set are constant, and cannot be changed once they're in the unordered_set, by default. You are trying to change the objects in the set, and the compiler is properly telling you that you can't do this.
The only possible way to do this correctly (explained only for educational purposes, because this is bad class design):
Explicitly declare the individual fields that can be modified in this manner as mutable.
Use a custom hash function with your unordered_set, and the hash function must exclude the value of mutable fields from the value of the calculated hash.
Otherwise, modifying the contents of the object in the set obviously changes its hash value, which will result in undefined behavior.
Again, this is explained for informational purposes only. This is not a good class design.
The clean way to do this would be to assign a unique identifier to each CreditCard (you know, like a credit card number?), and use an ordinary std::map, to look up CreditCards by their number.
It's not appropriate for ChangeBalance to have const semantics. By the very nature of it's name, you're modifying the object. Make the function non-const.
void ChangeBalance(int amount) {
balance += amount;
}
The other problem is that you didn't call your function correctly. You should instead do this:
iter->ChangeBalance(chargeAmount);
I will mention there are cases where you want to modify values in a const object, and there is a mutable type modifier for that. Do not use it to solve your current error, however!
void ChangeBalance(int amount) should not be const - it is changing the object.
The problem is before in the iterator: cards.find returns a const object, so you are not allowed to change it.
A way to resolve that is to make your cards set a set of pointers to cards, not of cards; or to use another way to find the matching card
Playing fast and loose with the C++ syntax in that thar code, Hoss. Plenty of errors wait around the corner
First Error:
iter->CreditCard::ChangeBalance(chargeAmount);
should be
iter->ChangeBalance(chargeAmount);
Straight-up bad syntax that likely results from flailing around because of the errors resulting from unordered_set having no idea how to hash a CreditCard. Give this a read: How do I use unordered_set? That said, unordered_set is probably not the right solution for this job. std::map<std::string, CreditCard> looks more on point.
Using the wrong solution to fix the above problem lead to the
Second Error:
void ChangeBalance(int amount) const
const on a method means the method cannot change the state of the object. in ChangeBalance balance += amount; attempts to change the state of the object by updating a member variable.
In addition, the compiler is going to HATE the CreditCard:: in this:
CreditCard::CreditCard(string in_cardHolder, string in_cardNumber, int in_limit) {
cardHolder = in_cardHolder;
cardNumber = stoll(in_cardNumber);
limit = in_limit;
balance = 0;
}
Another solution is to make the "balance" as a static member.
class CreditCard {
private:
string cardHolder;
unsigned long long cardNumber;
int limit;
static int balance;
....
}
And then initialize it in cpp file
int CreditCard::balance = 0;
This code may not be very secure. But this can be one of the workaround.
Lets say I have the following class:
static int counter = 0;
class Account {
public:
int ID;
int favNumber;
Account(int favNum) {
this->ID = ++counter;
this->favNumber = favNum;
}
};
Account user1(4);
Account user2(9);
Now both accounts user1 and user2 have different ID that is unique. Is there any way by knowing the ID of the account get the field of the object like "favNumber", if so how should it be implemented?
Something like getFieldById(int ID)
You may use std::map to do this :
#include <map>
class Account {
// Make attributes private. It is a better practice
int ID;
int favNumber;
static int s_Counter;
//^^^^^^^^^^^^^^^^^^^^^ It is better to move it as a static private member of Account
public:
Account(int favNum) {
this->ID = ++s_Counter;
this->favNumber = favNum;
}
// GETTERS
int GetFavNumber() const { return favNumber; }
int GetID() const { return ID; }
};
int Account::s_Counter = 0;
// ^^^^^^^^^^^^^^^^^^^^^^^^ Don't forget to initialize it
Account user1(4);
Account user2(9);
std::map<int, Account*> accounts;
accounts[user1.GetID()] = &user1;
accounts[user2.GetID()] = &user2;
// To get a favNum with some id :
accounts[id]->GetFavNumber();
But with this technique, be sure that the pointers are still valid ! If not, you could have bad surprises ...
What we have done in this previous code ?
We passed the attributs in private (better practice).
We created Getters to access them.
We passed the counter static variable as a static private member of Account.
We used std::map to have a listing of the accounts created and the keys are the IDs of the Accounts.
You can use
std::map<int, Account*>
to store a pointer to the accounts by their id. It's up to you to make sure the pointers remain valid. Alternatively, you could use
std::map<int, Account>
and let the map look after your accounts for you.
you can create a list and for each time you pass the constructor add the item to the list. then when a request get to your getFieldById search your list.
the list will have to be in a place you can search in and only be initiate once
You would need to central place to store all the objects that are going to be created and then search for the id there.
You could store them as
Plain old array
Search the entire list for your object of ID and then return the field
ID indexed array
array[ID] is the object you need, return the field
Hash(std::map) from ID to object
Similar syntax as ID indexed array but is a hash table lookup
Each have their pros and cons in simplicity, speed of search, memory used etc.
You could also store object pointers in the above.
To automate things, you can make the above list a private static member of your Account class, and add to it in the constructor.
I'm currently working on a college project with C++ and one of my assignments is to make a social network using inheritance and polymorphism. Currently I have a Node class that is used on a Map and Multimap (both are created manually and not used from the std). The node can hold two variables (key and data for example) and where I'm using it, the first variable can either be a pointer or a string (they let us use std::string).
The problem I'm having is that when I inherit from the "root" class (Object) and use "Object" as a data type for "key", I'm unable to pass a string created with the std as parameter to its constructor, because it doesn't inherit from my Object class. One solution is to implement my own string class and make it inherit from Object, but I was searching for other workarounds.
If there's any problem with the logic above, please tell me as I'm just beginning with C++.
EDIT 1 (some code for my Node):
class TempNode
{
private:
TempNode* next;
Key key;
T value;
public:
TempNode();
explicit TempNode(const Key thisKey, const T thisValue, TempNode* thisNext = NULL)
: key(thisKey)
, value(thisValue)
, next(thisNext)
{
}
inline Key getKey() { return key; }
inline T getValue() { return value; }
inline TempNode* getNext() { return next; }
inline void setNext(TempNode* thisNext) { next = thisNext; }
};
The string or Person types are currently used only in key, but that is with another implementation using templates (which works fine), but my teacher now requires us to apply inheritance to the entire project (to get used to it I guess).
To implement this using inheritance, you think of Key as a data type specifically designed as a key in your map/multimap implementation. Key inherits from Object, but it may provide its own, key-specific functions, such as – for example – a function repr() which generates a representation used by the map for some map-specific operations (maybe as a basis for hashing, or sorting or whatever).
The map/multimap must be used in such a way that the Key objects are stored as pointers (or std::unique_ptr, or std::shared_ptr, or whatever is appropriate), but not as copies of Key objects.
So we have:
struct Object
{
virtual ~Object()
{ }
};
/* Key class. Pointers of this type are inserted
into the map. */
class Key : public Object
{
public:
/* Must be supported by all keys: */
virtual std::string repr() const = 0;
};
We also assume there is a separate definition of Person objects:
struct Person : Object
{
Person(const std::string &name)
: name_(name)
{ }
std::string name_;
};
According to your specification, there are two flavours of Key: One that represents strings and must be initialized using a string, and another one that represents persons and must be initialized by a person pointer (I'll assume that the person-keys do not actually own these pointers, so you need to make sure the person objects they point to stay alive as long as the person-key exists).
We implement this by specializing Key into two derived classes, a PersonKey and a StringKey:
class PersonKey : public Key
{
public:
PersonKey(Person *person_ptr)
: Key() , person_ptr_(person_ptr)
{ }
virtual std::string repr() const
{
if (person_ptr_ != 0)
return std::string("Person/") + person_ptr_->name_;
else
return "<NUL>";
}
private:
Person *person_ptr_;
};
class StringKey : public Key
{
public:
StringKey(const std::string &str)
: Key() , str_(str)
{ }
virtual std::string repr() const
{
return str_;
}
private:
std::string str_;
};
When you make insertions into your map/multimap, you generate Key objects (which you represent as Key* or Key& or std::unique_ptr<Key>). When you want to insert a string, you generate them as StringKey objects, and when you want to insert them as person-pointers, you use PersonKey – but the data type of the key you insert will not reflect the specialization.
Here is an example of a general Key object (implemented as std::unique_ptr<Key>, but you may just use Key* if you are not afraid of memory leaks):
int main()
{
/* General key object: */
std::unique_ptr<Key> mykey;
/* Now it points to a string-key, initialized using
a string, as required: */
mykey.reset(new StringKey("hello"));
std::cout << "repr(mykey) == \""
<< mykey->repr()
<< '"'
<< std::endl;
/* Now the same key object is made to refer to
a person object: */
Person person("me");
mykey.reset(new PersonKey(&person));
std::cout << "repr(mykey) == \""
<< mykey->repr()
<< '"'
<< std::endl;
return 0;
}
Necessary headers for the code above are:
#include <iostream>
#include <memory>
#include <string>
(But memory is only required for my use of std::unique_ptr, which is not actually necessary to solve your problem.)
I think what you are really looking for are templates. Your solution with "root object" won't work as you can see with standard objects and external libraries but also you will not be able to use your containers with primitives (for example person id(as int) as key, and Person class as value).
With templates you can say what type you are going to work with at compile time and compiler will help you to obey your own rules. It can be declared like this:
template<class T1, class T2>
class Map{
T1 key;
T2 value;
(...)
}
Then you can use it more or less like this:
Map<std::String, int> phoneBook;
And compiler will guard you and warn, if you try to add, for example float instead of int, to you Map. But before you start coding I advice you to read some tutorials first, or maybe even some book on c++ in general. But if you want to start with generic right now, you can start her:
http://www.cplusplus.com/doc/tutorial/templates/
The only way you'd be able to store a string in your Object variable was if the string class inherited from your Object class, so you will have to implement your own String class unfortunately.
The real flaw here is that you are taking a Java/C# approach to design, where an Object variable can hold anything. In C++ the proper way to handle such things is through the use of templates, supposing your Map/Multimap/Node only need to hold one specific data type.
If your container needs to be able to hold any arbitrary data type, I would recommend using type erasure, although that can be a bit complicated for a beginner.
Given the following code:
struct Item
{
std::string name;
int someInt;
string someString;
Item(const std::string& aName):name(aName){}
};
std::unordered_map<std::string, Item*> items;
Item* item = new Item("testitem");
items.insert(make_pair(item.name, item);
The item name will be stored in memory two times - once as part of the Item struct and once as the key of the map entry. Is it possible to avoid the duplication? With some 100M records this overhead becomes huge.
Note:
I need to have the name inside the Item structure because I use the hashmap as index to another container of Item-s, and there I don't have access to the map's key values.
OK, since you say you are using pointers as values, I hereby bring my answer back to life.
A bit hacky, but should work. Basicly you use pointer and a custom hash function
struct Item
{
std::string name;
int someInt;
string someString;
Item(const std::string& aName):name(aName){}
struct name_hash
{
size_t operator() (std::string* name)
{
std::hash<std::string> h;
return h(*name);
}
};
};
std::unordered_map<std::string*, Item*, Item::name_hash> items;
Item* item = new Item ("testitem");
items.insert(make_pair(&(item->name), item);
Assuming the structure you use to store your items in the first place is a simple list, you could replace it with a multi-indexed container.
Something along thoses lines (untested) should fulfill your requirements:
typedef multi_index_container<
Item,
indexed_by<
sequenced<>,
hashed_unique<member<Item, std::string, &Item::name
>
> itemContainer;
itemContainer items;
Now you can access items either in their order of insertion, or look them up by name:
itemContainer::nth_index<0>::type & sequentialItems = items.get<O>();
// use sequentialItems as a regular std::list
itemContainer::nth_index<1>::type & associativeItems = items.get<1>();
// uses associativeItems as a regular std::unordered_set
Depending on your needs, you can use other indexings as well.
Don't store std::string name field in your struct. Anyway when you perform lookup you already know name field.
TL;DR If you are using libstdc++ (coming with gcc) you are already fine.
There are 3 ways, 2 are "simple":
split your object in two Key/Value, and stop duplicated the Key in the Value
store your object in a unordered_set instead
The 3rd one is more complicated, unless provided by your compiler:
use an implementation of std::string that is reference counted (such as libstdc++'s)
In this case, when you copy a std::string into another, the reference counter of the internal buffer is incremented... and that's all. Copy is deferred to a time where a modification is requested by one of the owners: Copy On Write.
No, there isn't. You can:
Not store name in Item and pass it around separately.
Create Item, ItemData that has the same fields as Item except the name and either
derive Item from std::pair<std::string, ItemData> (= value_type of the type) or
make it convertible to and from that type.
Use a reference to string for the key. You should be able to use std::reference_wrapper<const std::string> as key and pass key in std::cref(value.name) for key and std::cref(std::string(whatever)) for searching. You may have to specialize std::hash<std::reference_wrapper<const std::string>>, but it should be easy.
Use std::unordered_set, but it has the disadvantage that lookup creates dummy Item for lookup.
When you actually have Item * as value type, you can move the name to a base class and use polymorphism to avoid that disadvantage.
Create custom hash map, e.g. with Boost.Intrusive.
I'm writing a little address book application and have a design dilemna regarding the interface for the data source / backend.
I have the following abstract base class for data source classes:
class DataSource
{
private:
public:
virtual std::auto_ptr<Contact> getContact(int id) = 0;
virtual ContactRecordSet getAllContacts() = 0;
virtual bool addContact(const Contact& c) = 0;
virtual bool updateContact(int id, const Contact& c) = 0;
virtual bool deleteContact(int id)=0;
virtual ~DataSource() {};
};
Below is my record struct and tmy record set is a typedef to an STL vector of these objects.
class Contact
{
public:
std::string firstName;
std::string lastName;
std::string phoneNumber;
std::string address;
std::string email;
};
typedef std::vector<Contact> ContactRecordSet;
My question involves the return value type used for the DataSource::getContact() method and the DataSource::getAllContacts() method and the search method to be added soon that will get records based on a query.
DataSource::getContact() will return zero or 1 records since I'm looking up by unique id.
DataSource::getAllContacts() will return zero or more contacts.
The upcoming search method will return zero or more contacts.
As I have it now the getContact() method is returning an auto_ptr to a Contact because it seemed wasteful to return a ContactRecordSet if I know for sure they'll never be more than one and it allows me to return NULL if there is no record that has that id.
Would it be better to for getContact() to return a ContactRecordSet also, simply for the interface to remain consistent?
Part of me chafes at the idea of returning a data structure like that for a single object, but on the other hand it is more consistent and the semantics for checking if a value was found for that id seem more in line with the overall abstraction of the design (check length of returned recordset vs. check for a NULL auto_ptr).
What do you all think?
(Note - I'm aware I'm probably be over-engineering for a simple address book application but I want it to be easy to swap out different back ends (flat file, SQL, etc...) provided they implement the common interface. The goal is to practice good modular design & seperation of concerns.)
UPDATE
I suppose I could look at from the opposite perspective and make the multiple record methods return auto_ptrs to ContactRecordSet objectcs. That way a)it's consistent in that you're always getting a pointer to an object and b) you don't have the overhead of returning a std::vector if the record set is empty, simply return a NULL pointer.
I always follow the design principle of return the least complex thing that defines your object. Vectors are meant to hold lists of things not single item and while it might make the semantics symmetric it will undoubtedly be nonintuitive to another developer.
What's consistent about returning a plural type for a non-plural function?
I think you need to be working with a different definition of "consistency".