I'm new to programming and just come across this assignment
Create a container class family that holds an array of 20 person objects.
I have been looking on the internet as well as in my book, but I still can't figure out the difference between a Container Class and a Class in C++.
How could I create a family class and 20 person objects at the same time?
"Container class" is not some official term; it's just the word "class" with an English describing word next to it. The assignment is asking you to create a class that contains some other stuff; namely, an array of 20 person objects.
At its most basic, the result could be as simple as this:
class family
{
public:
person people[20];
};
In real life, you might do something like this instead:
#include <array>
using family = std::array<person, 20>;
It seems unlikely that every family (or even most families) has precisely 20 people in it, so I would personally end up just going with:
#include <vector>
std::vector<person> family;
… and manipulating the vector as appropriate.
C++ is an object oriented language that encourages you to "encapsulate". The first step of this is to group concepts into objects described in terms of their data values and the operations that can be performed on that data.
So one could define an Account class consisting of an id and balance and functions to deposit or withdraw currency. This definition forms the type, but when you "instantiate" (create an instance of) this type, i.e.
Account a;
then the variable a refers to an object of type Account in this scope.
Sometimes you need a class that can store and track objects of other types. This is what is sometimes referred to as a "container class" and typically it combines a store and a count.
Say we want to store some floats. We could just write:
float store[64];
std::cout << "Enter the first number: ";
std::cin >> store[0];
But how would we track how many floats we have? We would probably need a counter.,
float store[64];
int stored = 0;
std::cout << "Enter the first number: ";
std::cin >> store[0];
stored++;
std::cout << "Enter the second number: ";
std::cin >> store[1];
stored++;
This works, and it's not terribly difficult, but if you are writing a function that expects to take a store and it's size, how do you express that?
void myFunction(std::string label, float* store, int count);
This requires two arguments and it's not exactly explicit.
C++ is about encapsulation: this idea of a "store" with a count of the contents could be encapsulated into a class:
struct Store {
float store_[64] {};
int count_ {0};
};
this is a container. We can now write our function that takes an object that contains other values with a single parameter:
void myFunction(std::string label, Store& store); // & here = by reference
If this was 'C' you would write code that directly manipulated the values in the store:
store.store_[N] = 1;
store.count_++;
but that's messy, we didn't check there was room. In C++, we can encapsulate this into the class description with member functions and hide the member variables so that you have to go through our proscribed interface to manipulate the data.
#include <iostream>
class Store {
enum { MaxCount = 64 };
float store_[MaxCount] {};
size_t count_ = 0;
public:
// return the maximum number of elements we can store
size_t capacity() const { return MaxCount; }
// true/false: is the store empty?
bool empty() const { return count_ == 0; }
// return the current count
size_t size() const { return count_; }
bool add(float value) {
if (count_ >= capacity()) {
std::cerr << "store is full!\n";
return false;
}
store_[count_] = value;
++count_;
}
// reset
void clear() {
count_ = 0; // we don't actually need to change the store
}
// allow array-like usage
const float& operator[](size_t index) const { return store_[index]; }
float& operator[](size_t index) { return store_[index]; }
// provide bounds-checked array-ish access
float at(size_t index) const {
if (index >= count_)
throw std::invalid_argument("array index out of bounds");
return store_[index];
}
};
int main() {
Store store;
for (size_t i = 0; i < store.capacity(); ++i) {
std::cout << "Enter number #" << i << " or -ve to stop: " << std::flush;
float f = -1;
std::cin >> f;
std::cout << "\n" << f << "\n";
if (f < 0)
break;
store.add(f);
}
std::cout << "You entered " << store.size() << " values:";
for (size_t i = 0; i < store.size(); ++i) {
std::cout << ' ' << store[i];
}
std::cout << '\n';
}
Live demo: http://ideone.com/boE3Ki
They are telling you to create a class that acts as a container for an array. In programming, especially when you first start, you will see many elements called containers because your teacher wants you to view variables, classes, and arrays (among other programming tools) as containers for you to store data in.
Viewing programming this way makes is much easier to conceptualize information as you get into more complex ideas like pointers.
So, long answer short, create a class with the array inside it. If you are learning about constructors and overloading make sure you are initializing it based on the data you pass in. If not, it should be only a couple lines of code to complete the project.
Related
I need a structure to keep track of presence of some items. I just wanted to take an array a0....aN and mark the elements as a[0]=0,a[1]=0,a[2]=1........(a[i]=1 if the element is present,a[i]=0 if element is not present).
But the items range from -1000 to +1000. It can be done by putting the negative range from 1001 to 2000. I needed to know if there is any other data structure in c++ that can work like array and with negative indexes. Thank you for your time.
map is used for this only, to have key/index of any basic/user-defined data type. See - http://www.cplusplus.com/reference/map/map/
Example for your case:
#include <iostream>
#include <map>
#include <string>
int main ()
{
std::map<int, int> mymap;
mymap[-1]=1;
mymap[-2]=0;
mymap[-3]=1;
std::cout << mymap[-1] << '\n';
std::cout << mymap[-2] << '\n';
std::cout << mymap[-3] << '\n';
return 0;
}
Example for char:
#include <iostream>
#include <map>
#include <string>
int main ()
{
std::map<char,std::string> mymap;
mymap['a']="an element";
mymap['b']="another element";
mymap['c']=mymap['b'];
std::cout << "mymap['a'] is " << mymap['a'] << '\n';
std::cout << "mymap['b'] is " << mymap['b'] << '\n';
std::cout << "mymap['c'] is " << mymap['c'] << '\n';
std::cout << "mymap['d'] is " << mymap['d'] << '\n';
std::cout << "mymap now contains " << mymap.size() << " elements.\n";
return 0;
}
You an create your own data structure which supports -ve indexes. Just add an offset to the indexs while storing them in an array.
class MyArray {
int *arr;
public:
MyArray(int offset) {
arr = new int[2*offset]; // size must to double the offset
}
~MyArray(){
delete arr;
}
void add(int index, int val) {
arr[index + offset] = val;
}
void get(int index) {
return arr[index + offset];
}
}
Then you can just use your class to add and get elements with any index.
MyArray arr = MyArray(1000); // pass max -ve index as offset
arr.add(10, -150);
cout << arr.get(100);
I need a structure to keep track of presence of some items.
If what you want is set semantics, use a set data structure.
No need to implement a custom array wrapper.
You can use a std::set (or std::unordered_set) for that. Remember that "premature optimization is the root of all evil".
Insert the values that are there, leave out the values that are missing. No need to worry about negative indices.
You can use the methods std::set::find() or std::set::count() to check the presence of an item. Have a look at the documentation to find some example code.
If you later find it's a performance critical optimization, you can replace a std::set<int> with a data structure that you wrote yourself on the basis of an array of bits anytime. If it's not, doing so prematurely might turn out to be an unnecessary source of unexpected errors and a waste of time.
For reference:
http://en.cppreference.com/w/cpp/container/set
http://en.cppreference.com/w/cpp/container/unordered_set
http://en.cppreference.com/w/cpp/container/set/find
http://en.cppreference.com/w/cpp/container/set/count
How to check that an element is in a std::set?
Most efficient approach will be just shifting your array indexes so all of them are non-negative. In your case just use a[i+1000] and it will be sufficient.
If you really need to use negative indexes it is also possible.
C / C++ calculates memory address of array element using address of table and then adding index value to it. Using negative numbers just points to memory area placed before your table (which is not you normally want).
int a[2001];
int *b = &a[1000];
int x = b[-1000]; // This points to 1000 places before b which translates to a[0] (valid place)
Another approach will be using containers. Then any number can be translated to string and stored in proper container.
I think that the answer of #Rajev is almost fine. I have just replaced a plain array with a std::vector. Thus, the memory management is secure and copying and moving is easy.
template <typname T>
class MyArray {
private:
std::vector<T> arr;
public:
MyArray(int offset) {
arr.resize(2*offset); // size must to double the offset
}
void set(int index, int val) {
arr[index + offset] = val;
}
void get(int index) {
return arr[index + offset];
}
}
You can expand this further by overloading the operator [] of MyArray.
Databases usually look like this
┃Name|Age|..┃
┠────┼───┼──┨
┃John│025│..┃
┃Carl│033│..┃
┃....│...│..┃
In this case I mean a table with a fixed column size and a variable size of unsorted rows which can be addressed by an id.
Is there a data structure in C++11 (or earlier) that can represent data like this?
I thought of a couple of ways of cheating such a structure, but none of them is perfect.
1. Separate std::vector
std::vector<std::string> name;
std::vector<unsigned int> age;
// Write
name.push_back("John");
age.push_back(25);
// Read
std::cout << "The first entry is (" << name[0] << " | " << age[0] << ")\n";
Defining a table with many columns takes a lot of markup, though, and writing to it by calling push_back on each std::vector is really tedeous.
2. std::vector of std::tuple
(std::pair would be enough in this case)
std::vector<std::tuple<std::string, unsigned int>> table;
// Write
table.push_back(std::make_tuple("John", 25));
// Read 1
std::string name;
unsigned int age;
std::tie(name, age) = table[0];
std::cout << "The first entry is (" << name << " | " << age << ")\n";
// Read 2
enum
{
NAME = 0,
AGE
}
std::cout << "The first entry is (" << std::get<NAME>(table[0]) << " | "
<< std::get<AGE>(table[0]) << ")\n";
(Sorry, if I messed something up here; I've known about the existence of std::tuple since yesterday)
This is fine, but reading from it takes a lot of markup, this time, when you have to define new variables that you want to put the values in. You could just do the std::tie to whatever variable you need the values at, but that becomes unreadable. The second method is almost perfect, but using implicit enums is not something I want to do in C++11.
3. std::vector of std::array
enum
{
NAME = 0,
AGE
}
std::vector<std::array<std::string, 2> table;
// Write
table.push_back({"John", "25"});
// Read
std::cout << "The first entry is (" << table[0][NAME] << " | " << table[0][AGE] << ")\n";
This is also pretty good, but it suffers the same problem as 2.2 did. Also this only allows std::string values. In exchange it offers shorter and nicer syntax, though.
I suggest a std::vector<Record> to hold your records.
Use std::map<key, vector_index> as an index into your records. This will enable you to access records by different search criteria without sorting the vector all the time.
One thing you have't considered is using a std::vector of some kind of associative array, be that a std::map or std::unordered_map.
This would allow you to inspect a given item in the vector (or other sequential container) using the database column names
item["Name"]
item["Age"]
and so on. You would have to use a variant or something like boost::any for the value, obviously.
If you wish to talk to a database in C++, and know at compile time the type the columns have (as you appear to from your suggestions) it might be worth considering ways to generate code with appropriate structures for you directly from the data base scehema. There are a couple of questions around this topic already here and here. What if the db value is null?
Since this question is still seeing activity after 5 years, I will answer it myself with the 5 years of programming experience since then.
There is no point in using std::array or std::tuple to represent a row. A struct is more expressive and has less boilerplate.
The question basically asks the difference between AOS (array of structs), SOA (struct of arrays) and hash maps. The choice between the two depends on what you want to do with the data and how much data there is.
Data is loaded cache lines of 64 bytes with automatic cache preloading when iterating over the data. Since the hash map is non-contiguous and thus cannot take advantage of the caching mechanism, it should only be preferred when individual entries are accessed based on their keys rather than iterating over the entire data set.
The choice between AOS and SOA depends on whether the columns are iterated over individually or together. When iterating separately, by using SOA we can fit more of the same data on the cache line by not looking at the other arrays. For both AOS and SOA, the preferable data structure is std::vector which is a simple contiguous data structure and thus the processor has a lot of information for on-the-fly optimizations.
#-------------------------------------------
#Try with this piece of code based on <map>
#-------------------------------------------
#include <iostream>
#include <map>
using namespace std;
//---
class myBook
{
public:
string ISBN;
string Title;
int Pages;
myBook();
myBook(const myBook &);
~myBook(){};
myBook &operator=(const myBook &pTr);
int operator==(const myBook &pTr) const;
int operator<(const myBook &pTr) const;
};
myBook::myBook()
{
ISBN = "";
Title = "";
Pages = 0;
}
myBook::myBook(const myBook ©in)
{
ISBN = copyin.ISBN;
Title = copyin.Title;
Pages = copyin.Pages;
}
ostream &operator<<(ostream &output, const myBook &myBook)
{
output << myBook.ISBN << ':' << myBook.Title << ':' << myBook.Pages << std::endl;
return(output);
}
myBook& myBook::operator=(const myBook &pTr)
{
this->ISBN = pTr.ISBN;
this->Title = pTr.Title;
this->Pages = pTr.Pages;
return(*this);
}
int myBook::operator==(const myBook &pTr) const
{
if( this->ISBN != pTr.ISBN) return(0);
if( this->Title != pTr.Title) return(0);
if( this->Pages != pTr.Pages) return(0);
return(1);
}
//---------------------------
main()
{
map<string, myBook> BooksLibrary;
myBook book1;
//---
book1.ISBN="1243954-23";
book1.Title="Autobiography by Ben Franklin";
book1.Pages=432;
BooksLibrary["0001"] = book1;
//---
book1.ISBN="555-USA991";
book1.Title="I Robot by Isac Asimov";
book1.Pages=323;
BooksLibrary["0002"] = book1;
//---
for( map<string, myBook>::iterator ii=BooksLibrary.begin(); ii!=BooksLibrary.end(); ii++)
{
std::cout << (*ii).first << "|" << (*ii).second << endl;
}
//---
return(0);
}
I'm working on an assignment that involves creating a linked list of applicants (stored as a class), each having a value for ID, grade, years, and a numerical value (score). I need to write a function that searches this list for the applicant with the highest score and returns it. I was able to locate the highest score, but I would only return that value, and not the entire class of that specific applicant. After changing my code, I now get outputs for each of the 4 values (ID, grade, years, and score) but they all show 0, which is the initialized value. Here is all the relevant code.
#include <iostream>
#include <fstream>
#include <iomanip>
#include "applicant.cpp"
using namespace std;
struct Linkapp
{
Applicant person;
Linkapp *next;
};
class Linkthem
{
protected:
Linkapp *start;
public:
Linkthem(void);
void link(Applicant);
void printthem(void);
Applicant returnbest(void);
};
Applicant best;
Linkthem::Linkthem()
{
start = NULL;
};
void
Linkthem::link(Applicant one)
{
Linkapp *p;
p = new Linkapp;
p->person = one;
p->next = start;
start = p;
}
Applicant Linkthem::returnbest (void)
{
Linkapp *travel;
travel = start;
best = travel->person;
while (travel != NULL)
{
if (travel->person.return_value() > best.return_value())
best = travel->person;
travel = travel->next;
}
return best;
}
int
main()
{
ifstream infile;
Applicant fellow;
Linkthem mylist;
int id, yrs;
char knowledge;
cout.setf(ios::fixed);
cout.precision(2);
infile.open("applicnt.dat");
while ( !infile.eof() )
{
infile >> id >> knowledge >> yrs;
fellow.store_id(id);
fellow.store_skill(knowledge);
fellow.store_years(yrs);
mylist.link(fellow);
}
mylist.printthem();
cout << best.return_id() << ' ' << best.return_skill() << ' ';
cout << best.return_years() << ' ' << best.return_value() << endl;
fellow = mylist.returnbest();
return 0;
}
And from the "applicant.cpp"
class Applicant
{
protected:
int id;
char skill;
int years;
float value;
void calc_value(void); // <--- NOT for general use, called by return_value
public:
Applicant(void);
void store_id(int);
void store_skill(char);
void store_years(int);
int return_id(void);
char return_skill(void);
int return_years(void);
float return_value(void);
};
The problem definitely lies within my returnbest function, I'm just not sure where.
Just used std::max_element and std::list (which is doubly linked) or std::forward_list (which is singly linked), but I would probably default to std::vector instead:
std::max_element(begin(list), end(list), [](Applicant const& a, Applicant const& b) {
return (a.return_value() < b.return_value());
});
If the comparison function is intended for the entire Applicant class, it might make sense to define an operator< for said class.
As you seem to have to write your own list, some hints:
Linkapp appears to be an internal detail of your class Linkthem, so no bad idea to make it a private inner class.
returnbest (and probably printthem, if I may assume analogous implementation) will fail with segmentation fault, if your list is empty. You need to check, if start is null first:
if(start)
{
// rest of your code
}
else
{
// return some appropriate default
// throw exception
// or whatever else seems appropriate to you
}
You have quite uncommon names for your getters/setters. There are two main variants for naming:
int getId(); // or get_id(), but cammel casing is more common these days
void setId(int id);
// vs
int id();
void id(int id);
There is no need for Applicant best to be a global variable. Rather place it within the functions as local variables.
Now your actual problem:
Keep a close look on your main function - where do you call returnbest first? After you wrote out the values of best. This means that you never assigned any other value to best than the default. You have to place fellow = mylist.returnbest(); before the lines where you output best. But then use fellow for output instead of best, and you are free to make best a local variable within returnbest function...
mylist.printthem();
cout << best.return_id() << ' ' << best.return_skill() << ' ';
cout << best.return_years() << ' ' << best.return_value() << endl;
fellow = mylist.returnbest();
I don't know what you do in the printthem() member function, as you did not provide implementation of this function, but I assume you don't set variable best.So, you are printing an unset variable, which likely is initialized to zero.
What you want to do is more likely:
mylist.printthem();
best = mylist.returnbest();
cout << best.return_id() << ' ' << best.return_skill() << ' ';
cout << best.return_years() << ' ' << best.return_value() << endl;
I am trying to create a program that stores up to 50 players and the amount of wins they have. i.e. one use could be for for keeping track of sports teams and their amount of games won or something. However, I am having an issue when changing the player score. I am a beginner at C++, as you will probably be able to tell from my code. However, I have gone from knowing nothing whatsoever about coding to finishing three different tutorials and making a couple very simple command line programs in about a week. Any help as far as coding tips and how to make the scoreEdit() function work is thankfully accepted! Please find attached the players class, the scoreEdit() function, and my main function.
// for case 1. class that the scoreEdit() function uses!
class players
{
public:
void setName(string x)
{
name = x;
}
void addWin()
{
amtOfWins += 1;
}
void setWins(int x)
{
amtOfWins=x;
}
string getName()
{
return name;
}
int getWins()
{
return amtOfWins;
}
private:
string name;
int amtOfWins;
};
|
// for case 1. reads the file then stores each name in it's own player object and associates that with the amt of wins. Then rewrites all names and amtofwins to the file
void scoreEdit()
{
ifstream istats("stats.txt");
ofstream ostats("stats.txt");
if (istats.is_open() && ostats.is_open())
{
players player[50];
string tempName;
int tempWins;
while (istats >> tempName >> tempWins)
{
// reads in the name and amt of wins, and stores them in player object variables.
for (int x=0; x<50; x++)
{
player[x].setName(tempName);
player[x].setWins(tempWins);
}
}
string winner;
cout << "Who won?" << endl;
cin >> winner;
for (int x=0; x<50; x++)
{
if (player[x].getName()==winner)
{
player[x].addWin();
cout << "Ok. " << player[x].getName() << " has gained 1 point." << endl;
}
}
int x=0;
while (ostats << player[x].getName() << ' ' << player[x].getWins())
{
x++;
}
}
else
{
cout << "Quitting program. Stats could not be opened." << endl;
}
}
|
// main function
int main()
{
int x=0;
cout << "\n";
while (x==0) // not really sure if this is needed. Loops until case 4 or 5. Probably didn't need to use x. Oh well.
{
switch (choices())
{
case 0: // clears file
{
string decision;
cout << "ARE YOU SURE? This will wipe all data. (Type yes or no)\n" << endl;
cin >> decision;
if (decision=="yes")
{
clearStats();
cout << "STATS ARE WIPED.\n" << endl;
break;
}
else if (decision=="no")
{
cout << "Ok, stats will not be wiped.\n" << endl;
break;
}
else
{
cout << "Your input was not recognized. Stats will not be wiped.\n" << endl;
break;
}
}
case 1: // they want to add 1 to a player's score
{
scoreEdit();
break;
}
case 2: // they want to add a player
{
string name;
cout << "What is their name?" << endl;
cin >> name;
addPlayer(name);
break;
}
case 3: // they want to view the stats
{
readStats();
break;
}
case 4: // they want to quit. Simple! :)
{
return 0;
}
default: // means they did not input 1 2 3 or 4
{
cout << "Invalid input. Quitting program. Try again." << endl;
return 1;
}
}
}
}
EDIT: P.S. all my other functions/cases work. I just can't seem to find the problem with this one! Do I need to use a vector?
EDIT 2: Thanks for all the advice. Now, is there a way to check if the file still has data left to input? Right now, it inputs the lines just so that there are 50 player objects no matter what. Can I make a variable like int linesLeftInFile and use for (int x=0; x<linesLefInFile; x++)? Sorry for all the beginner questions.
You are trying to open the file twice, once for reading, once for writing, but in the same time.
...
ifstream istats("stats.txt");
ofstream ostats("stats.txt");
if (istats.is_open() && ostats.is_open())
...
When you open it for writing, like you do, the file content gets erased. The attempt to read the file will then fail, hence the big mess.
Open your stream first only to read, then close the stream, then open for write and put the results. Alternatively, you could consider an fstream with read and write.
By the way, it would be good practice for your players class to foresee a default constructor.
There are essentially two aspects:
A)
Am I creating my object arrays correctly? C++
which is related to persistence implementation and instance life-cycle issues, and
B)
EDIT: [...] Do I need to use a vector?
[...]
Right now, it inputs the lines just so that there are 50 player objects no matter what.
which is related to allocation and containers.
Both aspects of course intersect, e.g. a container like std::vector has much influence on the life-cycle of the instances it hosts.
Both aspects however have little to do with the functionality that is presented to the controller of the application, which is yet realized by the loop in main().
Thus, dealing with those aspects should be delegated to a class that separates them from the rest of the application.
Let's call that class-to-be PlayerDataBase.
PlayerDataBase will hold a container. But which?
Comlile-time fixed size arrays like
Player c_arra_players[50];
// or
std::array<Player,50> std_array_players;
are out of ansatz anyway; there is an external datum of arbitrary entity count which governs the multiplicity; thus the container must support run-time re-sizing.
A std::vector<Player> is plain and simple to use, but there's a seach to be performed
for (int x=0; x<50; x++) {
if (player[x].getName()==winner)
{
player[x].addWin();
cout << "Ok. " << player[x].getName() << " has gained 1 point." << endl;
}
}
which would have O(n) complexity on a sequential container like std::vector, whereas the same search on an associative like std::map (with the names used as keys) would be of O(log(n)) and about O(1) if std::unordered_map` would be used along with a perfect hash function.
On the other hand, adding and especially renaming players (use case: fixing typo in a nick) would be become more expensive for associative than sequential containers.
Especially for C++ learners it may be interesting to play a little with different internal storage representations, thus PlayerDataBase could be a template with a container template parameter.
There should only be one PlayerDataBase instance in the application and the controller should have a single point of access to that instance; thus it is meant to be a singleton.
The ifstream/ofstream/fstream issues can be dealt with quiet simple by PlayerDataBase - it's (private) ctor reads, or creates and reads, a stats file from an ifstream which is closed after the ctor completed.
Persisting the data is done by a flush() function, which opens an ofstream by using the _file_name member, writes, and that stream is closed on flush terminating.
///
///#brief modified singleton pattern
///
template<template<typename...> class Cont>
class PlayerDataBase : private players_container_adaptor<Cont> {
private:
using Traits = players_container_traits<Cont>;
public: // type interface
using container_type = typename Traits::players_container_type;
using Base = players_container_adaptor<Cont>;
struct no_such_player : public std::runtime_error {
no_such_player(const std::string& msg) : std::runtime_error(msg) {}
};
public: // creation interface
static PlayerDataBase& instance(const std::string& file_name = ::FILE_NAME)
throw (std::runtime_error) {
// automatically dtored
static PlayerDataBase _instance_(file_name); // throws
return _instance_;
}
public: // behaviour interface
void flush () const throw(std::ios::failure);
void addWin(const std::string& key) throw (no_such_player);
private: // types and function name resolution
using Adaptor = Base;
using Adaptor::getPlayer; // throws std::runtime_error
// container specific lookup,
using Adaptor::make_inserter; // std::copy(..,..,,make_inserter(Cont & c));
using Adaptor::emplace; // dispatches to container_specific emplace
using _asset_type = typename Traits::asset_type;
constexpr static auto BAD_AND_FAIL = std::ios::badbit | std::ios::failbit;
constexpr static auto BAD_ONLY = std::ios::badbit;
struct no_stats_file : public std::runtime_error {
no_stats_file(const std::string& msg) : std::runtime_error(msg) {}
};
private: // lifecycle interface
PlayerDataBase(const std::string&) throw (std::runtime_error);
~PlayerDataBase() noexcept;
// This is a singleton
PlayerDataBase(const PlayerDataBase&) = delete;
PlayerDataBase(PlayerDataBase&&) = delete;
PlayerDataBase& operator=(PlayerDataBase&&) = delete;
PlayerDataBase& operator=(const PlayerDataBase&) = delete;
PlayerDataBase& operator=(PlayerDataBase&) = delete;
private: // helpers
void create_data_file() const throw(std::ios::failure);
private: // state
container_type _players;
std::string _file_name;
}; // class PlayerDataBase
#include "playerdatabase.inl"
The required traits and adaptor templates could look like this:
template<template<typename...> class C> struct players_container_traits {};
// [...]
template<> struct players_container_traits<std::map> {
using players_container_type = std::map<std::string, Player>;
using player_ckv_type = players_container_type::value_type;
using player_kv_type = std::pair<std::string, Player>;
using asset_type = player_kv_type;
}; // struct player_container_traits<std::map>
// [...]
template<template<typename...> class C> struct players_container_adaptor{};
// [...]
template<> struct players_container_adaptor<std::map> {
using Traits = players_container_traits<std::map>;
using players_map_type = Traits::players_container_type;
static void post_ctor(players_map_type&) {
/* nop */
}
static Player& getPlayer(players_map_type& m, const std::string& key)
throw (std::runtime_error) {
auto it = m.find(key);
if (it == m.end())
throw std::runtime_error(key + " unknown");
return it->second;
} // addWin(players_map_t&,...)
static auto make_inserter(players_map_type& m)
-> decltype(std::inserter(m, m.begin())) {
return std::inserter(m, m.begin());
}
template<typename... Targs, typename K, typename... Args>
static void emplace(std::map<Targs...>& m, K&& k, Args&&... args) {
K _k = k;
m.emplace(std::piecewise_construct,
std::forward_as_tuple(_k),
std::forward_as_tuple(k, args...));
}
}; // template<> struct players_container_adaptor<std::map>
I postulated that the business class would be renamed to Player and gets a bunch of (noexcept) ctors and some related custom operators for << >>' forostreamresp.istream` output.
Happy hacking!
Addendum Jul 20th 2014
I don't see much reason for random sequence access anyway, thus if a sequential container would nevertheless be used (what I would not advocate due to the find complexity) then std::list would be more appropriate; that saves costs for possible complete, and expensive, re-allocations std::vector suffers from.
I'm working with a simple c++ program, with this class:
(I'm a very beginner programmer)
class Car{
private:
string newBrand;
int newMileage;
int newYear;
double newPrice;
(i didn't post the public classes)
bool sortByName(Car &CarVector)
{
return CarVector.getBrand() < CarVector.getBrand();
}
Main:
int main(){
vector<Car> CarVector;
ReadFile(CarVector);
ListCar(CarVector);
return 0;
}
LIst Car Functions, when i call the function "sort", to order my vector of objects by name:
void ListCar(vector<Car>&CarVector){
int i, op;
system("CLS");
sort(CarVector.begin(), CarVector.end(), sortByName);
cout << "MENU::CAR LIST BY NAME" << endl;
cout << ":Brand: \t:Mileage: \t:Year: \t\t:Price:" << endl;
for(i=0; i<CarVector.size();i++)
{
cout << CarVector[i].getBrand() << " \t\t";
cout << CarVector[i].getMileage() << " \t\t";
cout << CarVector[i].getYear() << " \t\t";
cout << CarVector[i].getPrice() << " \t\t";
cout << endl;
}
do
{
cout << endl << "1.Back: ";
cin >> op;
}while(op!=1);
}
I think that this program is supposed to work. Can you help me finding the error?
Best regards
In your compare function, you need to use two parameters: it should compare these objects with each other. Also, that's independent of any vector (it operates on elements of your vector), so you shouldn't name the arguments like that to avoid confusion.
So the function could look like this:
bool sortByName(Car &a, Car &b)
{
return a.getBrand() < b.getBrand();
}
Additionally, it's a good choice (but not always required) to add const to parameteres passed by reference in order to indicate that the function body doesn't modify them:
bool sortByName(const Car &a, const Car &b)
{
return a.getBrand() < b.getBrand();
}
But then it's necessary to put const at the end of the signature of the function Car::getBrand() in order to indicate that that function won't modify the object it operates on. This is called const-correctness. As mentioned before, that process is not always required (like when using std::sort) but it's a good style to have const-correctness.
Or you can use a lambda if your compiler supports it (you need to enable C++11 support):
std::sort(CarVector.begin(), CarVector.end(), [](const Car &a, const Car &b){
return a.getBrand() < b.getBrand();
});
Note that if you're going to compare cars always by name, it makes sense to implement an operator< for your class. When doing that, you don't need to specify a compare function in the call to std::sort, as those objects are then simply compared like a < b instead of yourFunction(a, b).
bool sortByName(Car &CarVector1,Car &CarVector2)
{
return CarVector1.getBrand() < CarVector2.getBrand();
}
use this sortByName
You have to pass two Car Objects in sortByName method to compare two Objects as :
bool sortByName(Car &C1,Car &C2)
{
return C1.getBrand() < C2.getBrand();
}
Check this link for more detail