I get some errors when I try to run the next code:
// tree.cpp
// compile with: g++ -std=c++11 tree.cpp -o tree
#include <iostream>
#include <future>
using namespace std;
int valtab[127]; // used for integer values of variables
class Tree; // forward declare
class Node {
protected:
Node() { use = 1; }
virtual void print(ostream &os) = 0;
virtual ~Node() { }
virtual int eval() = 0;
private:
friend class Tree;
friend ostream& operator<<(ostream&, const Tree&);
int use; // reference count
};
class Tree {
public:
Tree(int n); // constant
Tree(char id); // variable
Tree(char op, Tree t); // unary operator
Tree(char op, Tree left, Tree right); // binary operator
Tree(const Tree &t) { p = t.p; ++p->use; }
~Tree() { if (--p->use == 0) delete p; }
void operator=(const Tree &t);
int eval() { return p->eval(); }
private:
friend class Node;
friend ostream& operator<<(ostream &os, const Tree &t);
Node *p; // polymorphic hierarchy
};
void Tree::operator=(const Tree &t)
{
++t.p->use;
if (--p->use == 0)
delete p;
p = t.p;
}
ostream& operator<<(ostream &os, const Tree &t)
{
t.p->print(os);
return os;
}
class LeafNode: public Node {
private:
friend class Tree;
void print(ostream &os) = 0;
virtual int eval() = 0;
};
class IntNode: public LeafNode {
public:
int eval() { return n; }
private:
friend class Tree;
int n;
void print(ostream &os) { os << n ;}
IntNode(int k): n(k) { }
};
class IdNode: public LeafNode {
public:
int eval() { return valtab[name]; }
private:
friend class Tree;
char name;
void print(ostream& o) { o << name; }
IdNode(char id): name(id) { }
};
class UnaryNode: public Node {
public:
int eval();
private:
friend class Tree;
const char op;
Tree opnd;
UnaryNode(char a, Tree b): op(a), opnd(b) { }
void print(ostream& o) { o << "(" << op << opnd << ")"; }
};
int UnaryNode::eval()
{
switch (op) {
case '-': return (-opnd.eval());
case '+': return (+opnd.eval());
default: cerr << "no operand" << endl;
return 0;
}
}
class BinaryNode: public Node {
public:
int eval();
private:
friend class Tree;
const char op;
Tree left;
Tree right;
BinaryNode(char a, Tree b, Tree c): op(a), left(b), right(c) { }
void print(ostream &os) { os << "(" << left << op << right << ")"; }
};
int BinaryNode::eval()
{
switch (op) {
case '-': {
future<int> left = async(launch::async, left.eval());
future<int> right = async(launch::async, right.eval());
//return (left.get() - right.get());
}
case '+': return (left.eval() + right.eval());
case '*': return (left.eval() * right.eval());
default: cerr << "no operand" << endl;
return 0;
}
}
Tree::Tree(int n) { p = new IntNode(n); }
Tree::Tree(char id) { p = new IdNode(id); }
Tree::Tree(char op, Tree t) { p = new UnaryNode(op, t); }
Tree::Tree(char op, Tree left, Tree right) { p = new BinaryNode(op, left, right); }
int main()
{
valtab['A'] = 3; valtab['B'] = 4;
cout << "A = 3, B = 4" << endl;
Tree t1 = Tree('*', Tree('-', 5), Tree('+', 'A', 4));
Tree t2 = Tree('+', Tree('-', 'A', 1), Tree('+', t1, 'B'));
cout << "t1 = " << t1 << ", t2 = " << t2 << endl;
cout << "t1 = " << t1.eval() << ", t2 = " << t2.eval() << endl;
return 0;
}
This error I received:
error: 'class std::future<int>' has no member named 'eval'
I want to change the program so that expressions are evaluated in a parallel fashion. But I'm stuck with the future implementation, I do not quite understand how it works. Well, I understands how it works but I don't understand why I'm receiving these errors.
I hope some one can give me some tips or point me in the right direction.
You're trying to invoke a virtual member function as an async, but not properly providing the object to pin it against. Further, your use of same-id local vars, while not directly introducing an error, makes this incredibly hard to read.
I believe this is what you're shooting for:
auto eLeft = std::async(std::launch::async, std::bind(&Tree::eval, &this->left));
auto eRight = std::async(std::launch::async, std::bind(&Tree::eval, &this->right));
return (eLeft.get() - eRight.get());
Though, to be completely honest, this seems overkill. I see no sense in spinning up two asyncs just to then wait on both. Why not spin up one, then use the current thread to perform the other half of of your task:
auto eRight = std::async(std::launch::async, std::bind(&Tree::eval, &this->right));
return (left.eval() - eRight.get());
The parameter to std::async is a function (or more precisely, a Callable).
In particular, you do not invoke the function yourself (which would yield the return value). Invoking is done by std::async.
So these lines:
future<int> left = async(launch::async, left.eval());
future<int> right = async(launch::async, right.eval());
Should instead read something like:
future<int> left_result = std::async(launch::async, [this]() { return left.eval(); });
future<int> right_result = std::async(launch::async, [this]() { return right.eval(); });
Note that the function call is now wrapped in a lambda. Note also that you also reused the names for left and right in your original code, which is more trouble. I renamed them here to make it work.
As usual, since you now have concurrency in your program, you need to care about data races . Currently this seems to be fine, as all nodes in the tree are distinct, so the async calls don't share any data with each other. But keep it in mind for any future changes.
Related
I'm programming a family tree, and I was instructed to use lambda in the depth first search. I've tried to implement it, and I understand the basics of lambdas. I can't for the life of me understand how to make it work with the instructions I was getting from the teacher. Here is how I've tried to apply the code.
void depthFirst(const std::function<void(Node* )>& node) {
auto traverse = [](Node* node) {
node(this);
for( auto search: Person) {
search->depthFirst(node);
}
};
}
template<typename T>
class Node {
public:
explicit Node(const T& data, Node* parent = nullptr) : data_(data), parent_(parent) {}
explicit Node(T data): data_(std::move(data)) {}
virtual ~Node() = default;
T getData() const {
return data_;
}
void setData(T data) {
data_ = data;
}
Node *getParent() const {
return parent_;
}
void setParent(Node *parent) {
parent_ = parent;
}
bool leftExists() {
return this->left_ != nullptr;
}
void setLeft(const std::unique_ptr<Node> &left) {
left_ = left;
}
const std::unique_ptr<Node> &getLeft() const {
return left_;
}
bool rightExists() {
return this->right_ != nullptr;
}
const std::unique_ptr<Node> &getRight() const {
return right_;
}
void setRight(const std::unique_ptr<Node> &right) {
right_ = right;
}
private:
T data_; // node's data value with use of template
Node *parent_; // pointer to point at the parent node
std::unique_ptr<Node> left_;
std::unique_ptr<Node> right_;
};
template<typename T>
class Person {
public:
Person();
Person(std::string firstName, std::string lastName, int age, std::string gender, bool alive);
// setters
void setFirstName(const std::string &firstName);
void setLastName(const std::string &lastName);
void setGender(const std::string &gender);
bool isAlive() const;
void setAlive(bool alive);
void setAge(int age);
void setPerson();
// getters
const std::string& getFirstName() const;
const std::string& getLastName() const;
const std::string& getGender() const;
int getAge() const;
bool getAlive() const;
//operators
void displayPerson()const; // for testing atm
void setPerson(const Person& Person);
private:
std::string firstName_;
std::string lastName_;
int age_{};
std::string gender_;
bool alive_ = true;
};
// Functions that sets the data for the Person --->
template<typename T>
void Person<T>::setFirstName(const std::string &firstName) {
firstName_ = firstName;
}
template<typename T>
void Person<T>::setLastName(const std::string &lastName) {
lastName_ = lastName;
}
template<typename T>
void Person<T>::setGender(const std::string &gender) {
gender_ = gender;
}
template<typename T>
bool Person<T>::isAlive() const {
return alive_;
}
template<typename T>
void Person<T>::setAge(int age) {
age_ = age;
}
template<typename T>
void Person<T>::setAlive(bool alive) {
alive_ = alive;
}
// This is the default constructor, overload constructor and destructor for the person class --->
template<typename T>
Person<T>::Person(std::string firstName, std::string lastName, int age, std::string gender, bool alive) :
firstName_(std::move(firstName)), lastName_(std::move(lastName)), age_(age), gender_(std::move(gender)), alive_(alive) {}
template<typename T>
Person<T>::Person() {
}
// Functions that gets the data for the Person --->
template<typename T>
int Person<T>::getAge() const {
return 0;
}
template<typename T>
const std::string &Person<T>::getFirstName() const {
return this->firstName_;
}
template<typename T>
const std::string &Person<T>::getLastName() const
{
return this->lastName_;
}
template<typename T>
const std::string &Person<T>::getGender() const
{
return this->gender_;
}
template<typename T>
bool Person<T>::getAlive() const {
return false;
}
template<typename T>
class FamilyTree
{
public:
FamilyTree();
explicit FamilyTree(Node<T>* root); // Create new tree
FamilyTree(T data):
/*
void addNewPerson();
void addFather();
void addMother();
*/
void addNode(T data);
bool isEmpty();
private:
Node<T> *root_;
void addNode(Node<T>* root, T data);
};
template<typename T>
FamilyTree<T>::FamilyTree(Node<T> *root) {
this->root_ = root;
}
template<typename T>
bool FamilyTree<T>::isEmpty() {
return this->root_ == nullptr;
}
template<typename T>
FamilyTree<T>::FamilyTree() {
this->root_ = nullptr;
}
template<typename T>
void FamilyTree<T>::addNode(T data) {
if(root_ == nullptr)
root_ = std::make_unique(Node<T>(data));
else
addNode(root_, data);
}
//main
//just for test
void Person::displayPerson() const {
std::cout << "First Name: " << this->getFirstName() << std::endl;
std::cout << "Last Name: " << this->getLastName() << std::endl;
std::cout << "Age: " << this->getAge() << std::endl;
std::cout << "Gender: " << this->getGender() << std::endl;
std::cout << "Alive: " << this->getAlive() << std::endl << std::endl;
}
//main
int main(){
// Node test
Node node;
Node* setLeft(reinterpret_cast<Node *>(1));
Node* setRight(reinterpret_cast<Node *>(2));
std::cout << node.getData() << std::endl;
std::cout << node.getLeft() << std::endl;
std::cout << node.getRight() << std::endl;
//Person test
Person p0, p1("Robert", "Dane", 37, "Male", 1), p2("John", "Doe", 35, "Female", 1);
p0.displayPerson();
p1.displayPerson();
p2.displayPerson();
// FT test
return 0;
}
void ignoreLine() // inspiration from here: https://stackoverflow.com/questions/11523569/how-can-i-avoid-char-input-for-an-int-variable
{
std::cin.clear();
std::cin.ignore(INT_MAX, '\n');
}
void showMainMenu() // hold the output for the main menu
{
std::cout << "Welcome" << std::endl;
std::cout << "Please enter a number for your choice below:\n" << std::endl;
std::cout << "(1) Add new person to tree" << std::endl;
std::cout << "(2) Show information for a person" << std::endl;
std::cout << "(3) Print complete family-tree" << std::endl;
std::cout << "(4) Used for testing new choices" << std::endl;
std::cout << "(0) Quit" << std::endl;
std::cout << "\nYour choice: " << std::endl;
}
int main()
{
familyTree fT; // used to access/init. familytree class.
bool exit = true;
int option;
while (exit)
{
showMainMenu();
std::cin >> option;
while (std::cin.fail())
{
ignoreLine();
std::cout << "\nOnly a number between 0 and 10 is allowed: ";
std::cin >> option;
}
switch (option)
{
case 1:
fT.addNewPerson();
break;
case 2:
std::cout << "Enter name of person to show information: ";
int temp;
std::cin >> temp;
fT.show(fT.search(temp));
break;
case 3:
fT.printInOrder(fT.root, 0);
break;
case 4:
/* n/a */
break;
case 0:
exit = false;
break;
default: ;
}
std::cout << "\nPress enter to continue.." << std::endl;
ignoreLine();
}
return 0;
Old code that worked:
person *familyTree::traverseLeft(person *ptr, const std::string& person)
{
ptr = ptr->left;
while (ptr != nullptr)
{
if ((ptr->firstName) == person) {
return ptr;
}
else if (traverseRight(ptr, person) != nullptr)
{
return traverseRight(ptr, person);
}
else
{
ptr = ptr->left;
}
}
return nullptr;
}
person *familyTree::traverseRight(person *ptr, const std::string& person)
{
ptr = ptr->right;
while (ptr != nullptr)
{
if ((ptr->firstName) == person)
{
return ptr;
}
else if (traverseLeft(ptr, person) != nullptr)
{
return traverseLeft(ptr, person);
}
else
ptr = ptr->right;
}
return nullptr;
edit: The teacher told me that node(this); was supposed to point to the current node being searched. I may not have the most pedagogical correct teacher. But it is supposed to search the binary tree depth first, node for node. There is no use of vector or indexes, as I was told it was not needed. There is a class node and a class person that is implemented in to the node. If there is a better way of traversing a tree than this, feel free to let me know.
edited to add Person and Node.
edited to show old code that we got told to burn. I only got the instructions on lambda in person, but in short, was told to create lambda to use on a current node within a void function search, then go right, then go left. It could be reused in delete and other functions.
edited to add last of all code. Should I just go back to the old code (but less OOP) that I know compile and works? I got so much bad reviews on the old one that my group decided to start a new. But right now it's just a mess. (Keep in mind that the "new" code now is on different header files, so it might be more messy in regards to the console and main)
Is there a reason why you direct initialize your private variables in class Person as rvalues (ie. std::move?) ? std::string can bind permitted rvalues as long as they're const.
For instance the code below:
template<typename T>
Person<T>::Person(std::string firstName, std::string lastName, int age, std::string gender, bool alive) \
: firstName_(std::move(firstName)), lastName_(std::move(lastName)), age_(age), gender_(std::move(gender)), alive_(alive) {}
Could be:
template <typename T>
Person<T>::Person(std::string firstName, std::string lastName, int age, std::string gender, bool alive) \
: firstName_{firstName}, lastName_{lastName}, age_{age}, gender_{gender}, alive_{alive} {}
Making the the members in Person rvalues would be preparing them for a move, which it does not look like you're doing earlier in the code.
template <typename T>
void Person<T>::setFirstName(const std::string &firstName)
{
firstName_ = firstName;
}
The values are being passed as lvalue references in the function parameters of Person, which you are changing to rvalues in the constructor of said class. There is no need to do this. They are not meant to be temporary values. The use of {} instead of () eliminates the chance of implicit conversion (narrowing) on part of the members.
You're thinking inside out or upside down - you should pass a lambda (or another function) to this function, and this should apply that function in a depth-first manner.
You also need a helper function that takes a Node* that indicates the current node.
A very simple example, with a preorder traversal:
private:
void traverse(const std::function<void(Node*)>& action, Node* current)
{
if (current != nullptr)
{
action(current);
traverse(action, current->getLeft());
traverse(action, current->getRight());
}
}
public:
void traverse(const std::function<void(Node*)>& action)
{
traverse(action, root_);
}
And you are supposed to use it something like this:
FamilyTree tree = ... whatever ...;
auto process = [](const Node* p) { ... print p ... };
// This will now print in preorder.
tree.traverse(process);
#include <iostream>
class Piece {
public:
virtual char get()=0;
virtual ~Piece() {};
};
class One : public Piece {
public:
char get() { return '1'; }
};
class Two : public Piece {
public:
char get() { return '2'; }
};
class Tile {
private:
Piece* occ;
bool prs;
public:
Tile() { prs = false; }
void setOcc(Piece* p) { prs = true; occ = p; }
Piece& getOcc() { return *occ; }
bool getPrs() { return prs; }
void explicitDest() { if (prs) { delete occ; prs = false; } }
};
class Board {
private:
Tile tiles[2][2];
public:
Board() {
tiles[0][0].setOcc(new One());
tiles[0][1].setOcc(new Two());
tiles[1][1].setOcc(new One());
}
Tile getTile(int c, int r) { return tiles[c][r]; }
void move(Board* b, int c1, int r1, int c2, int r2) {
switch(b->tiles[c1][r1].getOcc().get()) {
case '1': b->tiles[c2][r2].setOcc(new One()); break;
case '2': b->tiles[c2][r2].setOcc(new Two()); break;
}
b->tiles[c1][r1].explicitDest();
}
void print() {
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
if (tiles[j][i].getPrs()) {
std::cout << tiles[j][i].getOcc().get() << " ";
} else {
std::cout << "- ";
}
}
std::cout << "\n";
}
std::cout << "\n";
}
Board* copyBoard() { return new Board(*this); }
};
int main()
{
Board* oldBoard = new Board();
std::cout << "Old board: \n";
oldBoard->print();
Board* newBoard = oldBoard->copyBoard();
std::cout << "New board: \n";
newBoard->print();
newBoard->move(newBoard, 0, 0, 1, 1);
std::cout << "Old board after move: \n";
oldBoard->print();
std::cout << "New board after move: \n";
newBoard->print();
delete[] newBoard;
}
This is an MRE to illustrate the methodology I've been using to do deep copies. It doesn't exactly work and is just to visualize how I've been doing things.
Using this example, is my method for deep copying strong? If not, what methods are available with a C++03 constraint to ensure a copy (and changes to the copy) do not reflect upon the original it's based upon?
In the code, I defined explicitDest() which is my way of explicitly (and only explicitly) calling the destructor as I need certain behavior only sometimes. Just in case people asked.
I'm not terribly familiar with copying, copy constructors, or abstract classes/methods if the code wasn't obvious.
You should implement copy constructors and copy assignment operators and take care when using new. You need one delete for each new - unless you surrender the pointer returned by new to a smart pointer. In C++03 you have std::auto_ptr that can be used to manage the memory resources for you.
Here's an example with comments inline:
#include <iostream>
#include <memory> // std::auto_ptr
#include <algorithm> // std::swap (<algorithm> in c++03, <utility> in >= c++11)
class Piece {
public:
// A virtual destructor to support deleting via base class pointer:
virtual ~Piece() {}
// You can't make constructors virtual, so add a clone()
// function for copy constuction through a base class pointer
virtual std::auto_ptr<Piece> clone() const = 0;
// renamed get() into symbol()
virtual char symbol() const = 0;
};
class One : public Piece {
public:
// Use the implicit copy constructor for One and return a (smart) pointer
// to the base class.
std::auto_ptr<Piece> clone() const {
return std::auto_ptr<Piece>(new One(*this));
}
char symbol() const { return '1'; }
};
class Two : public Piece {
public:
std::auto_ptr<Piece> clone() const {
return std::auto_ptr<Piece>(new Two(*this));
}
char symbol() const { return '2'; }
};
class Tile {
private:
std::auto_ptr<Piece> occ; // this now handles delete for you
public:
Tile() : occ(NULL) {} // default constructor
Tile(Piece* p) : occ(p) {} // put pointer in auto_ptr
// copy constructor, use the clone() function and conversion
// to bool operator below. If "o" doesn't have a Piece, initialize occ
// with an default constructed, empty, auto_ptr<Piece>.
Tile(const Tile& o) : occ(o ? o.occ->clone() : std::auto_ptr<Piece>()) {}
// ^
// |
// +--- conversion to bool in use
// copy assignment operator
Tile& operator=(const Tile& o) {
Tile tmp(o); // use the copy constructor above
occ = tmp.occ; // steal pointer from tmp
return *this;
}
// converting assignment operator
Tile& operator=(Piece* p) {
// delete the old pointer and replace it with p:
occ.reset(p);
return *this;
}
// Conversion to bool operator using std::auto_ptr's built in get()
// to tell us if we have a Piece or not.
operator bool() const { return occ.get() != NULL; }
// Add a symbol() function to hide the logic to determine if this Tile
// has a Piece or not.
char symbol() const {
// Check if there is a Piece in this Tile using the conversion
// to bool operator here too:
if(*this)
return occ->symbol();
else
return '-'; // no Piece here
}
};
// add support to stream a Tile to an ostream
std::ostream& operator<<(std::ostream& os, const Tile& t) {
return os << t.symbol();
}
class Board {
private:
Tile tiles[2][2];
public:
Board() {
// using the added operator() further down
(*this)(0,0) = new One;
(*this)(0,1) = new Two;
(*this)(1,1) = new One;
}
// Note that cols and rows in arrays are usually seen as reversed.
// tiles[2][2] usually means:
// tiles[<rows>=2][<cols>=2]
// getTile() replacements - the interface here is still (col, row)
// but it accesses the tiles[][] using the common form (row, col)
Tile& operator()(int c, int r) { return tiles[r][c]; }
Tile const& operator()(int c, int r) const { return tiles[r][c]; }
// moving by swapping tiles
void move(int c1, int r1, int c2, int r2) {
// using operator() and the standard function std::swap
std::swap((*this)(c1, r1), (*this)(c2, r2));
}
};
// Add a stream operator to not have to call print() explicitly when streaming
std::ostream& operator<<(std::ostream& os, const Board& b) {
for(int r = 0; r < 2; r++) {
for(int c = 0; c < 2; c++) {
// Use "Board::operator() const" and stream support for returned
// Tile.
os << b(c, r);
}
os << '\n';
}
os << '\n';
return os;
}
int main() {
// no need to "new" anything:
Board oldBoard;
Board newBoard(oldBoard); // use copy constructor
// use streaming operators
std::cout << "Old board: \n" << oldBoard;
std::cout << "New board: \n" << newBoard;
// using the getTile() replacement, Board::operator():
std::cout << "New board # tile 1,0: " << newBoard(1, 0) << " before move\n";
newBoard.move(0, 0, 1, 0);
std::cout << "New board # tile 1,0: " << newBoard(1, 0) << " after move\n\n";
std::cout << "New board after move:\n" << newBoard;
newBoard = oldBoard; // copy assignment operator
std::cout << "New board after reinit:\n" << newBoard;
}
Be aware of that there are many things inside example that would be done in a slightly different (more efficient) way in C++11 and later where std::unique_ptr, move semantics and extended initializer lists were added.
I am trying to access the function getnoOfkids() but even though its public I can't, why? I am only able to access the normal queue operations such as size,emplace, etc..
#include <iostream>
#include <queue>
using namespace std;
class Family
{
private:
int id, noOfElders, noOfKids;
public:
bool operator ==(const Family &f)
{
if ((this->id!= f.id) || (this->noOfElders != f.noOfElders)||(this->noOfKids != f.noOfKids))
{
return false;
}
return true;
}
bool operator !=(const Family &f)
{
return !(*this==f); //////////////////////
}
Family(int ide=0, int eld=0, int kid=0) {
noOfElders = eld;
noOfKids = kid;
id = ide;
}
Family(const Family &a) {
noOfKids = a.noOfKids;
noOfElders = a.noOfElders;
id = a.id;
}
Family operator=(Family const &a) {
this->id = a.id;
this->noOfElders = a.noOfElders;
this->noOfKids = a.noOfKids;
return *this;
}
int getnoOfkids() const {
return noOfKids;
}
int getnoOfElders() const {
return noOfElders;
}
int getid() const {
return id;
}
void setnoOfKids(int x) {
noOfKids = x;
}
void setnoOfElders(int x) {
noOfElders = x;
}
void setid(int x) {
id = x;
}
friend ostream & operator<<(ostream & out, const Family & a)
{
out << "The id of the travelers are: " << a.id << endl;
out << "The number of elders are: " << a.noOfElders << endl;
out << "The number of kids are: " << a.noOfKids << endl;
return out;
}
friend istream &operator >> (istream &in, Family &a) {
in >> a.id;
in >> a.noOfElders;
in >> a.noOfKids;
return in;
}
};
queue<Family> KidsQueue(queue<Family> &a, queue<Family> &b) {
queue <Family> newA,newB;
queue <Family> result;
queue <Family> newA,newB; queue <Family> result;
while(!a.empty())
{ if(a.getnoOfElders()) }
}
In KidsQueue(), your a parameter is an instance of std::queue holding elements of type Family. a itself is not a Family, so you can't call Family methods on it. You need to access individual Family objects inside of the queue to call Family methods on them, eg:
while (!a.empty())
{
if (a.front().getnoOfElders()) // <-- front() accesses the 1st Family object in the queue
{
...
}
a.pop();
}
Error
e/c++/v1/algorithm:642:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/utility:321:9: error:
field type 'Space' is an abstract class
_T2 second;
^
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/map:624:16: note:
Question
How can I define a std::vector of type Space which is an abstract class and then fill this vector with instances of the derived classes Empty, Snake, Ladder.
Context
I know abstract classes in C++ can not be instantiated. Instead I've read in several posts on this and other sites that you can create a collection of an abstract type if it the type is defined as a star * pointer or any of the <memory> managed pointer data types like std::unqiue_ptr<T>. I've tried to used shared_ptr<Space> in my case, but still unable to define the collection properly. I am compiled my code using g++ -std=c++17 main.cpp && ./a.out.
Code
#include <cstdlib>
#include <cmath>
#include <iostream>
#include <map>
#include <memory>
#include <typeinfo>
#include <queue>
#include <string>
#include <vector>
class Player
{
private:
int m_current_space = 1;
public:
Player() {}
void role_dice() {
m_current_space += floor( (rand()%10 + 1) / 3 );
}
int const get_current_space() {
return m_current_space;
}
void set_current_space(int current_space) {
m_current_space = current_space;
}
};
class Space
{
protected:
int m_id;
std::vector<Space> m_paths;
public:
Space() {} // requied to use [] operator in map
Space(int id) : m_id(id) {}
void add_path(Space& s) {
m_paths.push_back(s);
}
int get_id() {
return m_id;
}
virtual std::string class_type() = 0;
};
class Empty : public Space
{
public:
Empty(int id) : Space(id) {}
std::string class_type() {
return "Empty";
}
};
class Ladder : public Space
{
public:
Ladder(int id) : Space(id) {}
virtual void event(Player& p) {
p.set_current_space(1);
}
std::string class_type() {
return "Ladder";
}
};
class Snake : public Space
{
public:
Snake(int id) : Space(id) {}
virtual void event(Player& p) {
p.set_current_space(4);
}
std::string class_type() {
return "Snake";
}
};
class Board
{
private:
std::map<int, Space> m_board;
public:
void add_space(Space& s) {
m_board[s.get_id()] = s;
}
void draw_board() {
int i = 1;
for(auto const& [space_key, space] : m_board) {
if(i%3 == 0) {
std::cout << "○\n";
}
else if(typeid(space) == typeid(Snake)) {
std::cout << "○-";
}
else {
std::cout << "○ ";
}
++i;
}
}
void update_player_on_board(int position) {
int i = 1;
for(auto const& [space_key, space] : m_board) {
if(i%3 == 0) {
if (space_key == position) {
std::cout << "●\n";
}
else {
std::cout << "○\n";
}
}
else if(typeid(space) == typeid(Snake)) {
std::cout << "○-";
}
else {
if (space_key == position) {
std::cout << "● ";
}
else {
std::cout << "○ ";
}
}
++i;
}
}
const std::map<int, Space> get_board() {
return m_board;
}
friend std::ostream &operator<<(std::ostream& os, const Board& b) {
return os;
}
};
class GameStateManager
{
private:
std::string m_state = "game over";
bool m_playing = false;
public:
std::string const get_state() {
return m_state;
}
void set_state(std::string state) {
m_state = state;
}
};
int main()
{
std::cout << "Welcome to Bowser's 9 board game\n";
std::cout << "Start? y(yes) n(no)\n";
GameStateManager game_manager;
game_manager.set_state("playing");
auto space1 = std::make_shared<Space>(1);
auto space2 = std::make_shared<Space>(2);
auto space3 = std::make_shared<Space>(3);
auto space4 = std::make_shared<Space>(4);
auto space5 = std::make_shared<Space>(5);
auto space6 = std::make_shared<Space>(6);
auto space7 = std::make_shared<Space>(7);
auto space8 = std::make_shared<Space>(8);
auto space9 = std::make_shared<Space>(9);
std::vector<std::shared_ptr<Space>> v {
space1, space2, space3,
space4, space5, space6,
space7, space8, space9
};
Board bowsers_bigbad_laddersnake;
for(int i = 0; i < 10; ++i) {
bowsers_bigbad_laddersnake.add_space(*(v[i]));
}
bowsers_bigbad_laddersnake.draw_board();
Player mario;
int turn = 0;
while(game_manager.get_state() == "playing") {
std::cin.get();
std::cout << "-- Turn " << ++turn << " --" << '\n';
mario.role_dice();
bowsers_bigbad_laddersnake.update_player_on_board(mario.get_current_space());
if (mario.get_current_space() >= 9) {
game_manager.set_state("game over");
}
}
std::cout << "Thanks a so much for to playing!\nPress any key to continue . . .\n";
std::cin.get();
return 0;
}
You seem to have removed a lot of code to get into details here.
Have a Space pointer (smart or raw). Instantiate the specific space that you want, point to it with your pointer of type Space. Example std::shared_ptr<Space> pointerToSpace = std::make_shared<Snake> ("I'm a snake"); Now, without loss of generality, you can print the contents (of concrete type) with just the pointer to the space pointerToSpace->class_type(). Yes, you can have a collection of shared_ptrs in a container.
In the code bellow, instead of using new function "void print()", how can I use the overloaded "<<" operator in order to print the required information?
Or to be exact, where is the mistake here?
Overloaded << operator in one of the inherited classes:
friend ostream &operator<<(ostream &os, DigitSecret &s){
for(int i=0;i<s.n;i++)
os<<s.digits[i];
return os<<" Simple entropy: "<<s.simpleEntropy()<<" Total: "<<s.total();
}
void printAll (Secret ** secrets, int n) {
for(int i=0;i<n;i++){
cout<<secret[i] //This is printing an address, however that is not what i want.
secrets[i]->print(); //I want that to work like this.
}
}
The whole code: https://pastebin.com/MDCsqUxJ
I want line 134 and 143 to work correctly.
EDIT:
secret[i] is of type Secret*, you should derefence first and then your overload will get picked:
cout << *secret[i];
Side note: use std::vector instead of raw dynamic allocation.
See this snippet:
class base {
public:
virtual void print() = 0;
virtual std::ostringstream get_value() const = 0;
int get_id() const { return id_; }
protected:
int id_;
};
class A:public base {
public:
A(std::string val):val_(val){ id_ = 1; }
void print() override { std::cout << " I am A" << std::endl; }
std::ostringstream get_value() const { std::ostringstream ss; ss << val_; return ss; }
private:
std::string val_;
};
class B :public base {
public:
B(int val):val_(val) { id_ = 2; }
void print() override { std::cout << " I am B" << std::endl; }
virtual std::ostringstream get_value() const { std::ostringstream ss; ss << val_; return ss; }
private:
int val_;
};
std::ostream& operator << (std::ostream& os, const base* p)
{
std::string str;
if (p->get_id() == 1) {
str = ((A*)(p))->get_value().str();
os << "A " << str << "\n";
}
else
if (p->get_id() == 2) {
str = ((B*)(p))->get_value().str();
os << "B " << str << "\n";
}
return os;
}
void PrintAll(base** a)
{
for (int i = 0; i<2; i++)
std::cout << a[i];
}
int main()
{
base* a[2];
a[0] = new A("Hello");
a[1] = new B(10);
PrintAll(a);
return 0;
}
Output:
I Solved it this way:
void printAll (Secret ** secrets, int n) {
for(int i=0;i<n;i++){
DigitSecret* ds = NULL;
CharSecret* cs = NULL;
ds = dynamic_cast<DigitSecret*>(secrets[i]);
cs = dynamic_cast<CharSecret*>(secrets[i]);
if(ds!=NULL)
cout<<*ds<<endl;
else
cout<<*cs<<endl;
// secrets[i]->print();
}
}
Basically in this case, I have to use dynamic_cast with new pointer from the derived class, on each pointer from the array, and check if the pointer is !=NULL, and then use the overloaded operator on the dereferenced new pointer.