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);
Related
This will be a bit lengthy but anyhow i tried my best to simplify it using code.
I am building a binary tree but noticed something peculiar.
linked_binary_tree.h
#ifndef LINKED_BINARY_TREE_H
#define LINKED_BINARY_TREE_H
#include <iostream>
#include <list>
using namespace std;
typedef int Elem;
class LinkedBinaryTree {
protected:
struct Node {
Elem ele;
Node *par;
Node *left;
Node *right;
Node(): ele(), par(NULL), left(NULL), right(NULL) {}
};
public:
class Position {
friend LinkedBinaryTree;
private:
Node *v;
public:
Position(Node *_v=NULL): v(_v) { cout << "Position constr" << endl;}
Elem &operator*() const {
return v->ele;
}
Position left() const {
return Position (v->left);
}
Position right() const {
return Position (v->right);
}
Position parent() const {
return Position(v->par);
}
bool isRoot() const {
return v->par==NULL;
}
bool isExternal() const {
return v->left==NULL && v->right==NULL;
}
};
typedef list<Position> PositionList;
LinkedBinaryTree();
int size() const;
bool empty() const;
Position root() const;
PositionList positions(int trv) const;
void addRoot();
void expandExternal(const Position &p);
Position removeAboveExternal(const Position &p);
protected:
void preorder(Node *v, PositionList &pl) const;
void postorder(Node *v, PositionList &pl) const;
void inorder(Node *v, PositionList &pl) const;
private:
Node * _root;
int n;
};
#endif
linked_binary_tree.cc
#include <iostream>
#include <list>
#include "linked_binary_tree.h"
using namespace std;
LinkedBinaryTree::LinkedBinaryTree(): _root(NULL), n(0) {}
int LinkedBinaryTree::size() const {
return n;
}
bool LinkedBinaryTree::empty() const {
return size()==0;
}
LinkedBinaryTree::Position LinkedBinaryTree::root() const {
cout << "LinkedBinaryTree::root()" << endl;
return Position(_root);
}
void LinkedBinaryTree::addRoot() {
_root=new Node;
n=1;
_root->ele=n;
}
void LinkedBinaryTree::expandExternal(const Position &p) {
Node *v = p.v;
v->left=new Node;
v->left->par=v;
v->left->ele=++n;
v->right=new Node;
v->right->par=v;
v->right->ele=++n;
}
LinkedBinaryTree::PositionList LinkedBinaryTree::positions(int trv) const {
PositionList pl;
if (trv==1)
preorder(_root,pl);
else if (trv==2)
inorder(_root,pl);
else postorder(_root,pl);
return PositionList(pl);
}
void LinkedBinaryTree::preorder(Node *v, PositionList &pl) const {
pl.push_back(Position(v));
if (v->left!=NULL)
preorder(v->left,pl);
if (v->right!=NULL)
preorder(v->right,pl);
}
void LinkedBinaryTree::postorder(Node *v, PositionList &pl) const {
if (v->left!=NULL)
preorder(v->left,pl);
if (v->right!=NULL)
preorder(v->right,pl);
pl.push_back(Position(v));
}
void LinkedBinaryTree::inorder(Node *v, PositionList &pl) const {
if (v->left!=NULL)
preorder(v->left,pl);
pl.push_back(Position(v));
if (v->right!=NULL)
preorder(v->right,pl);
}
linked_binary_tree_main.cc
#include <iostream>
#include "linked_binary_tree.h"
using namespace std;
int main() {
LinkedBinaryTree lbt;
lbt.addRoot();
cout << "post addRoot()" << endl;
LinkedBinaryTree::Position pr = LinkedBinaryTree::Position(lbt.root()); // --> STATEMENT 1
cout << "post lbt.root()" << endl;
//LinkedBinaryTree::Position pr = lbt.root(); // --> STATEMENT 2
lbt.expandExternal(pr);
cout << "LinkedBinaryTree.size() :- " << lbt.size() << endl;
// 1-preorder 2-inorder 3-postorder
auto iter=lbt.positions(3);
auto cbeg=iter.cbegin();
auto cend=iter.cend();
for (; cbeg!=cend; cbeg++) {
cout << cbeg->operator*() << " ";
}
cout << endl;
return 0;
}
Results executing linked_binary_tree_main
post addRoot()
LinkedBinaryTree::root() --> STATEMENT 3
Position constr --> STATEMENT 4
post lbt.root()
LinkedBinaryTree.size() :- 3
Position constr
Position constr
Position constr
2 3 1
Note:
Statement 1
LinkedBinaryTree::Position pr = LinkedBinaryTree::Position(lbt.root()); // --> STATEMENT 1
a. The lbt.root() actually returned LinkedBinaryTree::Position instance.
b. There is no LinkedBinaryTree::Position constructor which takes in a Position instance. Instead it has the following:
Position(Node *_v=NULL): v(_v) { cout << "Position constr" << endl;}
which is it takes a pointer to a Node argument. Yet STATEMENT 1 works showing that LinkedBinaryTree::Position(Node *v) constructor is called.
c. If you comment out STATEMENT 1 and use STATEMENT 2 that of course would work as well.
So why does STATEMENT 1 works?
Appreciate any insight.
Thanks.
The constructor you're seeing is not the one you think it is.
The constructor in STATEMENT 1 is the (compiler-generated) copy-constructor.
The constructor printing the output Position constr happens in the LinkedBinaryTree::root function:
return Position(_root);
This was much easier to see once I created a more minimal example (together with some extra output).
Having a bit of a trouble with my supposed-to-be simple c++ code.
I've written a class to describe a Member (as a social media network member), a Member has std::list of followers and std::list of following, both of type Member*, and he can follow/unfollow other members.
Here is the full class:
Member.h
class Member {
private:
bool isCopy;
static int counter;
int id;
list<Member*> following;
list<Member*> followers;
public:
Member();
Member(const Member &p2);
~Member();
void follow(Member &member);
void unfollow(Member &member);
int numFollowers();
int numFollowing();
static int count();
bool operator==(const Member &other) const;
bool operator!=(const Member &other) const;
};
Member.cpp
int Member::counter = 0;
Member::Member()
{
counter++;
id = counter;
isCopy = false;
cout << "Constructor ID: " << id << endl;
}
Member::Member(const Member & p2)
{
this->id = p2.id;
isCopy = true;
}
Member::~Member()
{
cout << "Destructor ID: " << this->id << endl;
if (!isCopy)
{
counter--;
}
}
void Member::follow(Member &member)
{
if (find(following.begin(), following.end(), &member) == following.end())
{
following.push_back(&member);
member.followers.push_back(this);
}
}
void Member::unfollow(Member &member)
{
following.remove(&member);
member.followers.remove(this);
}
int Member::numFollowers()
{
return followers.size();
}
int Member::numFollowing()
{
return following.size();
}
int Member::count()
{
return counter;
}
bool Member::operator==(const Member & other) const
{
return this->id == other.id;
}
bool Member::operator!=(const Member & other) const
{
return this->id != other.id;
}
Here is a simple code causing me some problems:
Member member1, member2;
int main() {
test();
}
void test(){
Member member3;
member2.follow(member3);
member3.follow(member1);
}
After the test function has ended I can see the destructor of member3 being called, but in the main function if I watch the following list of member2 and the followers list of member1 (Both of type Member*) There is still pointer to the member3 address and he has some garbage values in it:
{0x00aff720 {isCopy=true (204) id=-858993460 following={ size=3302196 } ...}}
My goal here is that when the scope of member3 ends that the size of the following list of member2 and the size of the followers list of member1 will be 0.
Is there any way to address this issue?
Thanks everyone!
You need to clean up the links to this in your followers and following when an object is destroyed.
Something like
Member::~Member()
{
std::cout << "Destructor ID: " << id << std::endl;
if (!isCopy)
{
counter--;
}
std::for_each(followers.begin(), followers.end(), [this](Member * other){ other.following.remove(this); });
std::for_each(following.begin(), following.end(), [this](Member * other){ other.followers.remove(this); });
}
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.
###MyClass.h###
#ifndef _MyClass
#define _MyClass
#include <string>
using namespace std;
class MyClass
{
public:
MyClass(const string name, const string text);
void display(ostream & out) const;
MyClass & operator = (const MyClass & m);
int compare(const MyClass & right) const;
private:
string _name;
string _text;
};
bool operator < (const MyClass & left, const MyClass & right);
ostream & operator << (ostream & out, const MyClass & mc);
#endif
###Node.h###
#include <string>
#include "MyClass.h"
using namespace std;
typedef MyClass * DataType;
class Node
{
private:
DataType item; // data
Node * lchild; // left child pointer
Node * rchild; // right child pointer
public:
Node(DataType Item);
DataType getItem() const;
void setItem(const DataType & data);
Node* getLChild() const;
void setLChild(Node * p);
Node* getRChild() const;
void setRChild(Node * p);
virtual ~Node();
};
###BST.h###
#include "Node.h"
using namespace std;
class BST
{
private:
Node * root;
bool Search(const DataType item, Node * r) const;
void Insert (DataType item, Node * ptr);
void Destructor(const Node * r);
public:
BST();
bool IsEmpty() const;
void Insert(const DataType item);
bool Search(const DataType item) const;
virtual ~BST();
};
###MyClass.cpp###
#include <iostream>
#include "MyClass.h"
using namespace std;
MyClass::MyClass(const string name, const string text)
{
_name = name;
_text = text;
}
void MyClass::display(ostream & out) const
{
out << "Name: " << _name << endl;
out << "Text: " << _text << endl;
}
MyClass & MyClass::operator = (const MyClass & m)
{
if (this == & m)
return *this;
_name = m._name;
_text = m._text;
return *this;
}
int MyClass::compare(const MyClass & right) const
{
return _name.compare(right._name);
}
bool operator < (const MyClass & left, const MyClass & right)
{
return left.compare(right) > 0;
}
ostream & operator << (ostream & out, const MyClass & mc)
{
mc.display(out);
return out;
}
###Node.cpp###
#include "Node.h"
Node::Node(DataType Item):item(Item)
{
lchild = 0;
rchild = 0;
}
DataType Node::getItem() const
{
DataType anItem = item;
return anItem;
}
void Node::setItem( const DataType & data)
{
item = data;
}
Node* Node::getLChild() const
{
Node * p = lchild;
return p;
}
void Node::setLChild(Node * p)
{
lchild = p;
}
Node* Node::getRChild() const
{
Node * p = rchild;
return p;
}
void Node::setRChild(Node * p)
{
rchild = p;
}
Node::~Node()
{
}
###BST.cpp###
#include <iostream>
#include "BST.h"
using namespace std;
bool BST::Search(const DataType item) const
{
return Search(item, root);
}
bool BST::Search(const DataType item, Node * r) const
{
if(r != 0)
{
if (item == r->getItem())
return true;
else
{
if (item < r->getItem())
return Search(item, r->getLChild());
else
return Search(item, r->getRChild());
}
}
else
return false;
}
BST::BST()
{
root = 0;
}
bool BST::IsEmpty() const
{
return (root == 0);
}
void BST::Insert(const DataType item)
{
if(root == 0)
root = new Node(item);
else
Insert(item, root);
}
void BST::Insert(DataType item, Node * ptr)
{
if (item < ptr->getItem())
{
if (ptr->getLChild() == 0)
ptr->setLChild(new Node(item));
else
Insert(item, ptr->getLChild());
}
else
{
if (ptr->getRChild() == 0)
ptr->setRChild(new Node(item));
else
Insert(item, ptr->getRChild());
}
}
void BST::Destructor(const Node * r)
{
if(r!=0)
{
Destructor( r->getLChild());
Destructor( r->getRChild());
delete r;
}
}
BST::~BST()
{
Destructor(root);
}
###main.cpp###
#include <iostream>
#include "MyClass.h"
#include "BST.h"
using namespace std;
void main()
{
MyClass * mc1 = new MyClass("Tree","This is a tree");
MyClass * mc2 = new MyClass("Book","This is a book");
MyClass * mc3 = new MyClass("Zoo","This is a zoo");
BST * tree = new BST();
tree->Insert(mc1);
tree->Insert(mc2);
tree->Insert(mc3);
cout << boolalpha << ("Book" < "Tree") << endl;
cout << (mc2 < mc1) << endl;
cout << (tree->Search(new MyClass("Book",""))) << endl;
}
Result is true false false
I don't know what's wrong with my operator overloading? (mc2 should
less than mc1)
I'm not sure if this is correct for searching a "MyClass" node in a BST?
and the result is "not found"....I traced it into "BST.cpp",
and found that the problem also occurs at " if (item < r->getItem()) "
Can anyone help me or give me a hint....thank you so much!
Here you are just comparing pointers, i.e memory addresses:
cout << (mc2 < mc1) << endl;
To compare the objects, you need to dereference the pointers:
cout << ((*mc2) < (*mc1)) << endl;
In your code snippet, there is no reason for mc1, mc2, etc. to be pointers, so you could avoid the problem by creating objects on the stack directly:
MyClass mc1("Tree","This is a tree");
and so on. I would even go further and say that you should only dynamically allocate objects with new if you really are sure you need to and have good reasons not to allocate automatically on the stack. And if you really must use dynamically allocated pointers, have a look at C++ smart pointers.
Suppose I have two classes a base class and an inherited class as follows:
class Magic : public Attack
{
public:
Magic(int uID, std::string &name) : Attack(ActionType::MagicAction, uID, name)
{
}
};
class MacroMagic : public Magic
{
MacroMagic(int uID) : Magic(uID, std::string("Testing"))
{
}
void PreUse() override
{
std::cout << "Test" << std::endl;
}
}
I have a shared_ptr to an instance of magic that I would like to copy, but at runtime I will not know whether that pointer points to an instance of Magic, MacroMagic or anything that may have inherited from Magic. I want to be able to copy the object pointed to by the shared_ptr like so:
Battles::magic_ptr mgPtr = MagicNameMap[name];
if (mgPtr.get() != nullptr)
{
return magic_ptr(new Magic(*mgPtr));
}
return mgPtr;
where magic_ptr is a typedef for a shared_ptr around the Magic Class. I could do it by specifying a virtual copy function and calling that, but I'd like to make it less obtuse and easier to maintain. I assume I can do this by a copy constructor, but I'm unsure how to in this instance. The way I have it now, the pointer returned by the above code will not call the override pReUse() function.
A bit of guidance would be much appreciated, thanks
I could do it by specifying a virtual copy function and calling that, but I'd like to make it less obtuse and easier to maintain.
you're working against the language.
you could accomplish what you are after, but i don't recommend it, and it is certainly not easier to setup or maintain than virtual Magic* clone() = 0.
perhaps you could outline the problem that brought you to this conclusion, and then we can help from there. there are usually alternatives which don't fight the language.
EDIT
here's a way around it using an external function table (t_magic_operation_table). you can apply and create several function tables and keep them around. since they exist in the magic object as a single pointer, then you can make these tables quite large (if needed). if your magic types can use the same data/members, then that is one approach. be careful: i threw this together suuuper-fast. it demonstrates the technique, but it's pretty bad otherwise:
#include <iostream>
#include <string>
namespace MONSpiel {
inline unsigned prndm(const unsigned& max) {
return 1 + arc4random() % max;
}
class t_ghoul;
class t_biclops;
class t_magic;
class t_hero {
t_hero();
t_hero(const t_hero&);
t_hero& operator=(const t_hero&);
public:
t_hero(const std::string& inName) : d_name(inName) {
}
const std::string& name() const {
return this->d_name;
}
template<typename TEnemy, typename TMagic>
void attack(TEnemy& enemy, TMagic& magic) const {
if (enemy.isDead()) {
return;
}
enemy.hit(magic.power());
if (enemy.isDead()) {
std::cout << this->name() << ": see you in the prequel...\n\n";
}
else {
std::cout << this->name() << ": have you had enough " << magic.name() << ", " << enemy.name() << "???\n\n";
}
}
/* ... */
private:
const std::string d_name;
};
class t_enemy {
t_enemy();
t_enemy(const t_enemy&);
t_enemy& operator=(const t_enemy&);
public:
t_enemy(const std::string& inName) : d_name(inName), d_lifePoints(1000) {
}
virtual ~t_enemy() {
}
const std::string& name() const {
return this->d_name;
}
bool isDead() const {
return 0 >= this->d_lifePoints;
}
const int& lifePoints() const {
return this->d_lifePoints;
}
void hit(const int& points) {
this->d_lifePoints -= points;
}
/* ... */
private:
const std::string d_name;
int d_lifePoints;
};
class t_ghoul : public t_enemy {
public:
static int MaxDaysAwake() {
return 100;
}
t_ghoul(const std::string& inName) : t_enemy(inName), d_bouyancy(prndm(100)), d_proximityToZebra(prndm(100)), d_daysAwake(prndm(MaxDaysAwake())) {
}
const int& bouyancy() const {
return this->d_bouyancy;
}
const int& proximityToZebra() const {
return this->d_proximityToZebra;
}
const int& daysAwake() const {
return this->d_daysAwake;
}
private:
int d_bouyancy;
int d_proximityToZebra;
int d_daysAwake;
};
class t_biclops : public t_enemy {
public:
t_biclops(const std::string& inName) : t_enemy(inName), d_isTethered(prndm(2)), d_amountOfSunblockApplied(prndm(100)) {
}
const bool& isTethered() const {
return this->d_isTethered;
}
const int& amountOfSunblockApplied() const {
return this->d_amountOfSunblockApplied;
}
private:
bool d_isTethered;
int d_amountOfSunblockApplied;
};
class t_magic_operation_table {
public:
typedef void (*t_ghoul_skirmish_function)(t_magic&, t_ghoul&);
typedef void (*t_biclops_skirmish_function)(t_magic&, t_biclops&);
t_magic_operation_table(t_ghoul_skirmish_function ghoulAttack, t_biclops_skirmish_function biclopsAttack) : d_ghoulAttack(ghoulAttack), d_biclopsAttack(biclopsAttack) {
}
void willSkirmish(t_magic& magic, t_ghoul& ghoul) const {
this->d_ghoulAttack(magic, ghoul);
}
void willSkirmish(t_magic& magic, t_biclops& biclops) const {
this->d_biclopsAttack(magic, biclops);
}
private:
t_ghoul_skirmish_function d_ghoulAttack;
t_biclops_skirmish_function d_biclopsAttack;
};
class t_action {
public:
typedef enum t_type {
NoAction = 0,
MagicAction,
ClubAction,
ClassAction
} t_type;
};
class t_attack {
public:
t_attack(const t_action::t_type& actionType, const int& uID, const std::string& inName) : d_actionType(actionType), d_uID(uID), d_name(inName) {
}
virtual ~t_attack() {
}
void reset() {
/* ... */
}
const std::string& name() const {
return this->d_name;
}
private:
t_action::t_type d_actionType;
int d_uID;
std::string d_name;
};
class t_magic : public t_attack {
t_magic();
t_magic(const t_magic&);
t_magic& operator=(const t_magic&);
static void GhoulSkirmishA(t_magic& magic, t_ghoul& ghoul) {
magic.d_accuracy = ghoul.bouyancy() + prndm(16);
magic.d_power = ghoul.proximityToZebra() + prndm(43);
}
static void GhoulSkirmishB(t_magic& magic, t_ghoul& ghoul) {
magic.d_accuracy = ghoul.bouyancy() / magic.flammability() + prndm(32);
magic.d_power = t_ghoul::MaxDaysAwake() - ghoul.daysAwake() + prndm(23);
}
static void BiclopsSkirmishA(t_magic& magic, t_biclops& biclops) {
if (biclops.isTethered()) {
magic.d_accuracy = 90 + prndm(16);
}
else {
magic.d_accuracy = 40 + prndm(11);
}
magic.d_power = biclops.amountOfSunblockApplied() + prndm(17);
}
static void BiclopsSkirmishB(t_magic& magic, t_biclops& biclops) {
if (biclops.isTethered()) {
magic.d_accuracy = 80 + prndm(80);
}
else {
magic.d_accuracy = 50 + prndm(50);
}
magic.d_power = 80 + prndm(30);
}
const t_magic_operation_table* NextOperationTable() {
static const t_magic_operation_table tables[4] = {
t_magic_operation_table(GhoulSkirmishA, BiclopsSkirmishA),
t_magic_operation_table(GhoulSkirmishB, BiclopsSkirmishB),
t_magic_operation_table(GhoulSkirmishB, BiclopsSkirmishA),
t_magic_operation_table(GhoulSkirmishA, BiclopsSkirmishB)
};
return & tables[arc4random() % 4];
}
public:
t_magic(const int& uID, const std::string& inName) : t_attack(t_action::MagicAction, uID, inName), d_power(-1), d_accuracy(-1), d_operationTable(0) {
}
int flammability() const {
return prndm(73);
}
int power() const {
return this->d_power;
}
void reset() {
t_attack::reset();
this->d_power = -1;
this->d_accuracy = -1;
this->d_operationTable = 0;
}
private:
/* assigns this->d_operationTable */
void updateOperationTableForAttack() {
this->d_operationTable = NextOperationTable();
}
public:
void heroWillAttack(const t_hero& hero, t_ghoul& ghoul) {
this->updateOperationTableForAttack();
this->d_operationTable->willSkirmish(*this, ghoul);
std::cout << hero.name() << " vs. " << ghoul.name() << "(lp:" << ghoul.lifePoints() << ")";
this->printState();
}
void heroWillAttack(const t_hero& hero, t_biclops& biclops) {
this->updateOperationTableForAttack();
this->d_operationTable->willSkirmish(*this, biclops);
std::cout << hero.name() << " vs. " << biclops.name() << "(lp:" << biclops.lifePoints() << ")";
this->printState();
}
void printState() {
std::cout << ": Magic { Power: " << this->d_power << ", Accuracy: " << this->d_accuracy << ", Operation Table: " << this->d_operationTable << "}\n";
}
private:
int d_power;
int d_accuracy;
const t_magic_operation_table* d_operationTable;
};
template<typename TEnemy>
void AttackEnemyWithMagic(t_hero& hero, TEnemy& enemy, t_magic& magic) {
if (!enemy.isDead()) {
magic.heroWillAttack(hero, enemy);
hero.attack(enemy, magic);
magic.reset();
}
}
inline void PlayIt() {
t_hero zoe("Zoe");
t_hero aragosta("Aragosta");
t_ghoul ghoul0("Al Paca");
t_ghoul ghoul1("Spud");
t_ghoul ghoul2("Sleepy");
t_biclops biclops("Scimpanzè");
t_magic hemlock(59, "hemlock");
t_magic babyPowder(91, "baby powder");
for (size_t idx(0); idx < 1000; ++idx) {
AttackEnemyWithMagic(zoe, ghoul1, hemlock);
AttackEnemyWithMagic(aragosta, biclops, babyPowder);
AttackEnemyWithMagic(zoe, ghoul2, hemlock);
AttackEnemyWithMagic(aragosta, ghoul0, babyPowder);
}
}
} /* << MONSpiel */
int main(int argc, char* const argv[]) {
#pragma unused(argc)
#pragma unused(argv)
MONSpiel::PlayIt();
return 0;
}
another option is to simply create stores (e.g. a vector), each with a different magic type. then fill a vector to point to objects in those stores. that way, you can just create one contiguous allocation for each type and randomize and weigh as needed. this is useful if your magic objects' sizes vary considerably.