I'm doing an assignment, where I have to create a database in c++ for a sport organization, without using STL (I created my own list and String). The database is keeping the data in a doubly linked list, where besides the nodes, there is the data, which are the teams. Of course there is not just one type of teams, there is currently three. These objects, inherited the teams objects. I have made everything work, except reading the text file, and thus creating objects.
I have already tried, to create a marker, which is the first piece of information the program will read, which will decide which of the three classes needs to be created, then reads the other data, to create the new object, then put it in the end of the doubly linked list. Unfortunately it does'nt work, instead it does nothing, and continue the whole programme, like nothing happened.
void hozzaad(ListaElem *s, team *data) { ///adding to the end
ListaElem *iter = s;
while (iter->kov=NULL)
{
iter = iter->kov;
}
ListaElem *uj = new ListaElem(data);
uj->elozo = iter;
iter->kov = uj;
}
void listaz(ListaElem *s) { //print out all that is in the list
if (s == NULL) {
std::cout << "Ures lista" << std::endl;
return;
}
ListaElem *iter = s;
while (iter!=NULL)
{
iter->adat->kiirt(std::cout);
iter = iter->kov;
}
}
void listament(ListaElem *s, const char *a) { //this one creates the file
std::ofstream file;
file.open(a);
ListaElem *iter = s;
if (file.is_open()) {
while (iter != NULL) {
file << iter->adat->Getclub()<< "\n";
file << iter->adat->Getname() << "\n" << iter->adat->Getmember()<< "\n";
if (iter->adat->Getclub() == 1) {
file << iter->adat->Getsupport() << "\n";
}
if (iter->adat->Getclub() == 2) {
file << iter->adat->Getpompom() << "\n";
}
if (iter->adat->Getclub() == 3) {
file << iter->adat->Getname1() << "\n" << iter->adat->Getname2() << "\n";
}
iter = iter->kov;
}
}
else
{
std::cout << "Nem tudom kinyitni a file-t";
}
file.close();
return;
};
void test4() { // the basic test
Handball c("Kezes HC", 21, 50000);
team adat("", 0);
ListaElem *egyik = new ListaElem(&adat);
hozzaad(egyik,&c);
ListaElem *uj = new ListaElem(&adat);
listament(egyik, "test.txt");
std::ifstream file;
file.open("test.txt");
if (!(file.is_open())) {
std::cout << "hiba\n";
return;
}
int micsoda;
while (file >> micsoda);
{
if (micsoda == 1) {
String beolvas("");
int m;
int d;
getline(file, beolvas);
file >> m;
file >> d;
Handball ujh(beolvas, m, d);
hozzaad(uj, &ujh);
beolvas = "";
}
if (micsoda == 2) {
String fbeolvas("");
int fm;
String e1("");
String e2("");
getline(file, fbeolvas);
file >> fm;
getline(file, e1);
getline(file, e2);
football ujh(fbeolvas, fm, e1, e2);
hozzaad(uj, &ujh);
}
if (micsoda == 3) {
String bbeolvas("");
int bm;
int bd;
getline(file, bbeolvas);
file >> bm;
file >> bd;
Basketball ujh(bbeolvas, bm, bd);
hozzaad(uj, &ujh);
}
}
std::cout << "OK" << std::endl;
listaz(uj);
file.close();
std::cout << "OK" << std::endl;
}
test4() expects to go out like:
OK
Kezes HC 21 50000
OK
Shortening the code to the absolutely necessary:
if (micsoda == 1)
{
Handball ujh;
hozzaad(uj, &ujh);
} // at this point in code, your object ujh runs out of scope!!!
At the time the object runs out of scope, it is destroyed (you will notice if you add some output statement in the class' destructor...) and any pointers to it get invalid; especially, those in the list get invalid (one speaks of dangling pointers – or references, for which the same can happen). Now using them results in undefined behaviour – which means anything could happen. If you're unlucky (or lucky, depending on point of view), your program even crashes.
Be aware that, due to lacking important parts of your code, I am assuming that you only store pointers to the objects in your ListaElem class, which is pretty likely, though, as the classes to be stored are polymorphic...
What you need, though, are objects living longer than just while the programme is in the if block. Assuming we target some real-life scenario, just moving the objects out of the if clauses is no option as we might need more than one single object of the same type. So you will create the objects dynamically. However, then you'll have to deal with memory management and the ownership question as well. Easiest to handle the memory management part is using smart pointers. Ownership? Well, it appears reasonable to me to assume the list being the sole owner of the objects, so you could have:
class ListElem
{
std::unique_ptr<Team> m_data;
public:
ListElem(std::unique_ptr<Team> data) // accepting a unique_ptr already here indicates
// clearly that the item will grab ownership
: m_data(std::move(data)) // unique_ptr is only movable, not copiable!
{ }
}
Then you can change your code above to:
if (micsoda == 1)
{
int n, m; // sample data
hozzaad(uj, std::make_unique<Team>(n, m); // arguments are passed directly to
// the constructor of class Team
}
OK, you are not allowed to use STL; then you'd write your smart pointers on your own, as well as make_unique and move template functions (just as you did for list and string already). On cppreference, you can find a sample implementation, e. g. for std::make_unique. Don't just copy/paste the code, but understand it first, and best re-write it from scratch on your own, otherwise you won't be learning anything (same applies for my code above). Or maybe you ask your teacher if she/he makes an exception on STL for smart pointers.
Final advice: Operating on contiguous memory in general is much faster than operating on memory potentially distributed all over your system RAM. So you might consider rather re-implementing std::vector instead of std::list.
Related
I am going through "Programming principles and practice using C++" by Bjarne Stroustrup, and I am trying to complete this exercise:
Define a File_handle class with a constructor that takes a string argument (the file name), opens the file in the constructor, and closes it in the destructor.
I was wondering if it was possible to give the user the possibility to extract the reference/pointer to the stream to operate on it and leave to the class the responsibility to close it when out of scope.
It is probably not good practice, but I got interested in the behavior I get (see the code in main()), it doesn't seem possible to access the stream from outside the class, but it works perfectly inside it.
There is probably something very obvious to a trained eye that I am missing, but since I am still trying to learn, I would like to understand why and how things work a bit deeper with stream classes
Also, I would like to know how a more experienced programmer would approach the problem.
I already got similar problems a couple of times, but I couldn't find satisfying answers after a good bit of google-fu, and reading the C++ reference manual.
class File_handle
{
fstream file;
public:
File_handle(string s)
:file{s}
{
if (!file) error("File not found!");
cerr << "Constructed File_handle" << endl;
}
File_handle(const File_handle&) = delete;
File_handle& operator=(const File_handle&) = delete;
~File_handle()
{
cerr << "Destroyed File_handle" << endl;
file.close();
return;
}
fstream& attach() { return file; };
fstream* attach_p() { return &file; };
void print(ostream& os)
{
while (file) {
char c = file.get();
cout << c;
}
}
};
int main()
{
File_handle stream {"TextFile.txt"};
fstream& f = stream.attach();
cout << f; // invalid operands to binary expression
cout << f.get(); // output => 35
fstream* fp = stream.attach_p();
cout << *fp; // invalid operands to binary expression
cout << fp->get(); // output => 35
stream.print(cout); // works fine, prints the whole file
cout << endl;
}
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.
I'm writing a program that creates a vector of pointer-to-objects.
How do I access the individual objects that the pointers reference from the pointer-to-objects vector?
I'm trying to call the speak() function in the class Object for each of the objects that the pointers inside the vector reference.
Thank you for your time
class Object
{
public:
void speak()
{
cout<<"Hello!"<<endl;
}
};
int main()
{
int choice;
vector<Obj*> objVector; //create empty vector of "pointer-to-object"
Object* ptrObj; //point to object
while (choice!=5)
{
cout <<"1.Create Object\n";
cout <<"2.Destroy Object\n";
cout <<"3.Print number of existing Objects\n";
cout <<"4.Tell existing Objects to say Hello\n";
cout <<"5.Quit Program"<<endl;
cout <<"Please enter your choice: ";
cin >> choice;
if (choice==5)
cout <<"\nProgram is quitting\n"<<endl;
else if (choice==1)
{
ptrObj= new Object;
ObjVector.push_back(ptrObj); //adding an Object object
}
else if (choice==2) //remove object
{
objVector.pop_back();
}
else if (choice==3)
{
cout <<"\nThere are " << objVector.size() <<" objects total.\n" << endl;
}
else if (choice==4)
{
for (int i=0; i<objVector.size(); i++)
{
????????????
}
}
}
return 0;
}
In your existing code, you can access the pointer exactly the way you use it elsewhere in code:
Object* obj = objVector[i];
obj->speak();
// or, simply:
objVector[i]->speak();
Using the operator -> is simply another way to say (*objVector[i]).speak().
Alternatively, the idiomatic approach to writing the loop would look like this:
for(vector<Object*>::iterator it = objVector.begin(); it != objVector.end(); ++it) {
// iterators work like another level of pointers, and need to be dereferenced:
(*it)->speak();
}
If your compiler supports C++11, you can rewrite the loop like this:
for(auto it = std::begin(objVector); it != std::end(objVector); ++it) {
(*it)->speak();
}
Or like this, using range-based for, which dereferences the iterator for you:
for(auto& obj : objVector) {
obj->speak();
}
As an aside, there are cases where you will not be sure whether objVector[i] is even in the vector at all, and accessing it may crash your program or even cause demons to fly forth from your nasal cavity.
For added safety, you can reference positions in your vector with the at function, like so:
try {
for (int i=0; i<objVector.size(); i++)
{
Object* obj = objVector.at(i);
obj->speak();
}
} catch (const std::out_of_range& ex) {
cerr << "no object at position " << i << " in objVector" << endl;
cerr << "objVector says " << ex.what() << endl;
}
Notice, though, that this is a lot slower, although it gives you a chance to handle the problem in the catch block. The try block will run the loop and stop and run the catch block if the at function throws an exception - which will be an exception of type out_of_range. Note also that using [i] will not do the same thing, because it does not throw an exception - it doesn't even bother to check if i is within the length of the vector. This happens to also be why [i] is faster than .at(i).
Finally, also notice that the loops using iterators cannot encounter this particular problem, so long as you don't try to use the iterators after adding or removing something from the vector.
The easiest way is to use *(objVector[i])
To access speak, objVector[i]->speak is just shorter.
You can dereference them with *. Like *(ObjVector[i])
But if you just need to call a method of object you can do it with ->
ObjVector[i]->speak()
Unrelated to the question, but I drop some comment to revise the program.
As others pointed out, you can call object function from pointer contained on vector with doing objVector[i]->speak().
However, as #greyfade pointed out, there is leaking memory issue. You have to delete object when the object is created by new. You can delete object by delete like this,
Object* ptr = objVector.back();
objVector.pop_back();
delete ptr;
To erase memory leaking issue, you can store Object object directly in objVector instead of Object*. In this way, you don't have to worry about deleting objects. You can do like this,
int main()
{
int choice;
vector<Object> objVector; //create empty vector of "pointer-to-object"
while (choice!=5)
{
cout <<"1.Create Object\n";
cout <<"2.Destroy Object\n";
cout <<"3.Print number of existing Objects\n";
cout <<"4.Tell existing Objects to say Hello\n";
cout <<"5.Quit Program"<<endl;
cout <<"Please enter your choice: ";
cin >> choice;
if (choice==5)
cout <<"\nProgram is quitting\n"<<endl;
else if (choice==1)
{
objVector.emplace_back(); //adding an Object object
}
else if (choice==2) //remove object
{
objVector.pop_back();
}
else if (choice==3)
{
cout <<"\nThere are " << objVector.size() <<" objects total.\n" << endl;
}
else if (choice==4)
{
for (auto& obj : objVector)
{
obj.speak();
}
}
}
return 0;
}
This code is using c++11 feature. You can add object by calling emplace_back and delete object by just calling pop_back(). Isn't this sweet?
And one more thing. You forgot some code on header. This code cannot be compiled without these headers,
#include <iostream>
#include <vector>
using namespace std;
I'd happy if this code helps you.
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.
This code uses a while loop to get user input and execute the appropriate command - I've reduced it to 2 commands for sake of brevity.
The Oblock object is created correctly (command "O"), as is the pointer to the base class. It appears that the calls to both objects work correctly as well.
However, after returning to the while loop, the pointer to the object appears to be lost, and attempting to access its members (command "t") causes a segfault.
I've included the example code below - my questions are afterwards.
#include<vector>
#include<iostream>
#include<string.h>
using namespace std;
class Tetramino {
private:
int squareSize;
vector<string> myShape;
public:
void setValues(int size) {
squareSize = size;
myShape = vector<string> ((size*size), ".");
}
string getValues(int i) {
return myShape[i];
}
int getSize() {
return squareSize;
}
};
class Oblock : public Tetramino {
public:
Oblock() {
setValues(2);
}
};
main () {
string input;
bool runProgram = true;
Tetramino *pBlock;
while (runProgram) {
cin >> input;
if (input == "O")
{
Oblock myBlock;
cerr << "0thi: " << myBlock.getValues(0) << endl;
Tetramino *pBlock = &myBlock;
cerr << "0thi: " << pBlock->getValues(0) << endl;
}
if (input == "t")
{
cerr << "0thi: " << pBlock->getValues(0) << endl;
}
}
return 0;
}
Are objects deconstructed upon exiting the if statements?
Is there perhaps a better way to repeatedly get user input?
Thanks in advance for any advice! I searched for questions similar to this one and couldn't find one appropriate for my needs.
Tetramino *pBlock is local within its scope. You're shadowing over the one in main with the one within the if.
Also, myBlock is local and will be destructed - you'll have a dangling pointer. You should allocate with new (and delete...)
Instead of Tetramino *pBlock = &myBlock; do pBlock = new Oblock; when you handle the "O" input (and handle delete pBlock of the previous one).
if (input == "O")
{
Oblock myBlock;
cerr << "0thi: " << myBlock.getValues(0) << endl;
Tetramino *pBlock = &myBlock;
cerr << "0thi: " << pBlock->getValues(0) << endl;
}
An object with automatic storage duration (commonly called a function-local variable) has a lifetime beginning after its declaration and ending at the end of the nearest enclosing block } token. So myBlock is destroyed at the end of this if statement, and can't be used again.
Also note that you have declared two different pointers named pBlock. Assigning the inner one does nothing to the earlier one, which is still uninitialized.
Answering your first question: Yes, the statement
Oblock yblock;
Creates an instance of Oblock on the stack. It is destroyed when the code leaves the respective block, so any pointers to it become invalid after that. To create an object that lives as long as you wish, use new to create the object on the heap.
Your pointer becomes garbage after myBlock goes out of scope (where the if statement closes) which will be causing the segmentation fault when you try to access it later
if(input == "O"){
Oblock myBlock;
}
^^^^ right here myBlock becomes garbage
Also, if a user inputs "t" before "O" it will cause a segmentation fault because they will be trying to access an uninitialized pointer. You should probably get that looked at.