I'm creating a program that will simulate a race between various runners, using behavior classes to implement different types of runner movements.
To do this, an abstract MoveBehaviour class will be implemented, along with several other concrete sub-classes (etc. WalkBehaviour, SleepBehaviour, SlideBehaviour).
The abstract MoveBehaviour class will require a pure virtual move() function, and the appropriate behaviour will be implemented in the concrete sub-classes. This move() function computes a new position newPos for the runner, given its current position oldPos, and the move() function will return a short, text description of the move in the log parameter (Etc. "walk forward 1 step") , which will be printed to the screen in a later step. I feel as if I'm not returning my log values in these functions correctly, and this relates to another issue.
In the update() function in Runner.cc, I'm supposed to randomly select the runner’s next move behaviour. This involves a new walking behaviour 40% of the time, a sleeping behaviour 40% of the time, and a slide behaviour 20% of the time. I'm supposed to use the new behaviour object to compute a new position that will be stored in the newPos parameter, and then I am to document the move in the runner’s current log data member. Etc if the runner is named Timmy, and the new move behaviour is walking, the current log data member will store the string “Timmy walked one step.”
Going back to my log, I wasn't sure how I would access the string that I declared in each of the move functions for every behaviour class. I noticed there is a getLog() function in Runner.cc, but I feel like it doesn't make sense to use that. This makes me thing I wasn't supposed to declare the "walked one step" strings and such in the move classes but rather in the update classes instead.
Additionally, I don't understand how to get the new behaviour object to compute a new position that will be stored in the newPos parameter and would appreciate some help with that as well.
For getting the log values, I'm just printing the runner's name below and my attempt was going to append whatever was in the log value to this sentence, but I wasn't sure how to access the log values.
I can include the SleepBehaviour and SlideBehaviour classes if needed, but they are practically identical to WalkBehaviour and I figured only one example was needed.
Runner.cc
void Runner::update(Position& newPos){
int r;
r = random(100) + 1;
if(r <= 40){
WalkBehaviour* walk = new WalkBehaviour;
}else if (r <= 40){
SleepBehaviour sleep = new SleepBehaviour;
}else{
SlideBehaviour* slide = new SlideBehaviour;
}
cout << name << endl;
}
Position.cc
#include <iostream>
using namespace std;
#include <string>
#include "Position.h"
Position::Position(int i1, int i2) : row(i1), column(i2){
}
Position::getRow(){ return row; }
Position::getColumn(){ return column; }
void Position::setRow(int r){ row = r; }
void Position::setColumn(int c){ column = c; }
MoveBehaviour.h
#ifndef MOVEBEHAVIOUR_H
#define MOVEBEHAVIOUR_H
#include <iostream>
#include "Position.h"
using namespace std;
class MoveBehaviour
{
public:
virtual void move(Position&, Position&, string&) = 0;
virtual ~MoveBehaviour() = default;
};
class WalkBehaviour : public MoveBehaviour{
public:
virtual void move(Position&, Position&, string&);
virtual ~WalkBehaviour();
};
class SleepBehaviour : public MoveBehaviour{
public:
virtual void move(Position&, Position&, string&);
virtual ~SleepBehaviour();
};
class SlideBehaviour : public MoveBehaviour{
public:
virtual void move(Position&, Position&, string&);
virtual ~SlideBehaviour();
};
WalkBehaviour.cc
#include <iostream>
using namespace std;
#include <string>
#include "MoveBehaviour.h"
void WalkBehaviour::move(Position& oldPos, Position& newPos, string& log) {
newPos.setColumn(oldPos.getColumn() + 1);
newPos.setRow(oldPos.getRow());
log = (" walked one step \n");
}
WalkBehaviour::~WalkBehaviour(){}
First, you need to actually use polymorphism by declaring a pointer to a base MoveBehaviour object that you let point to a derived instance.
Additionally, you need to make sure that you don't leak memory, so I chose std::unique_ptr which is automatically freed upon function exit.
Next, you can simply pass an empty std::string for the function to assign the log to, and use a std::stringstream to construct a line with the name with the move description. The output of this stringstream is then added to the log member in one go.
void Runner::update(Position& newPos) {
int r;
r = random(100) + 1;
std::unique_ptr<MoveBehaviour> movement;
if(r <= 40) {
movement = make_unique<WalkBehaviour>();
} else if (r <= 80) {
movement = make_unique<SleepBehaviour>();
} else {
movement = make_unique<SlideBehaviour>();
}
std::string moveLog;
movement->move(currPos, newPos, moveLog);
currPos = newPos;
std::stringstream ss;
ss << name << " " << moveLog << std::endl;
log += ss.str();
}
Here:
if(r <= 40){
WalkBehaviour* walk = new WalkBehaviour;
}else if (r <= 40){
SleepBehaviour sleep = new SleepBehaviour;
}else{
SlideBehaviour* slide = new SlideBehaviour;
}
you are creating new behaviors and immediately leaking them. You should have assign them ti Runner's MoveBehaviour* behaviour;, deleting its old behavior first:
delete behaviour;
if(r <= 40){
behaviour = new WalkBehaviour;
}else if (r <= 40){
behaviour = new SleepBehaviour;
}else{
behaviour = new SlideBehaviour;
}
Your WalkBehaviour::move() uses log correctly (except that you don't need to enclose text literal into ()
Related
I have 1 question because I am pretty curious how to handle with such problem.
I have base class called "Pracownik" (Worker) and 2 subclasses which are made from public Pracownik;
- Informatyk (Informatic)
- Księgowy (Accountant)
Writing classes is easy. Made them pretty fast but I have small problem with main because I am helping friend with program but I was not using C++ for a while. So:
This is my header file "funkcje.h"
#include <iostream>
using namespace std;
class Pracownik
{
private:
string nazwisko;
int pensja;
public:
Pracownik(string="",int=0);
~Pracownik();
string getNazwisko();
int getPensja();
friend double srednia_pensja(int,Pracownik);
};
class Informatyk : public Pracownik
{
private:
string certyfikat_Cisco;
string certyfikat_Microsoft;
public:
Informatyk(string="",int=0, string="", string="");
~Informatyk();
void info();
};
class Ksiegowy : public Pracownik
{
private:
bool audytor;
public:
Ksiegowy(string="",int=0, bool=false);
~Ksiegowy();
void info();
};
double srednia_pensja(int,Pracownik);
These are definitions of my functions "funkcje.cpp"
#include "funkcje.h"
Pracownik::Pracownik(string a,int b)
{
nazwisko=a;
pensja=b;
}
Pracownik::~Pracownik()
{
}
string Pracownik::getNazwisko()
{
return nazwisko;
}
int Pracownik::getPensja()
{
return pensja;
}
Informatyk::Informatyk(string a, int b, string c, string d) : Pracownik(a,b)
{
certyfikat_Cisco=c;
certyfikat_Microsoft=d;
}
Informatyk::~Informatyk()
{
}
Ksiegowy::Ksiegowy(string a, int b, bool c) : Pracownik(a,b)
{
audytor=c;
}
Ksiegowy::~Ksiegowy()
{
}
void Informatyk::info()
{
cout<<"Nazwisko pracownika: "<<Pracownik::getNazwisko()<<endl;
cout<<"Pensja pracownika: "<<Pracownik::getPensja()<<endl;
cout<<"Certyfikat Cisco: "<<certyfikat_Cisco<<endl;
cout<<"Certyfikat Microsoft: "<<certyfikat_Microsoft<<endl;
}
void Ksiegowy::info()
{
cout<<"Nazwisko pracownika: "<<Pracownik::getNazwisko()<<endl;
cout<<"Pensja pracownika: "<<Pracownik::getPensja()<<endl;
cout<<"Audytor: ";
if(audytor)
cout<<"Tak"<<endl;
else
cout<<"Nie"<<endl;
}
double srednia_pensja(int a,Pracownik *b)
{
return 0;
}
And finally main!
#include <iostream>
#include "funkcje.h"
using namespace std;
int main()
{
Pracownik lista[10];
Pracownik *lista_wsk = new Pracownik[10];
Informatyk a("Kowalski1",1000,"Cisco1","Microsoft1");
Informatyk b("Kowalski2",2000,"Cisco2","Microsoft2");
Informatyk c("Kowalski3",3000,"Cisco3","Microsoft3");
Ksiegowy d("Kowalski4",4000,1);
Ksiegowy e("Kowalski5",5000,0);
lista[0]=a;
lista[1]=b;
lista[2]=c;
lista[3]=d;
lista[4]=e;
Informatyk *ab = new Informatyk("Kowalski1",1000,"Cisco1","Microsoft1");
Informatyk *ac = new Informatyk("Kowalski2",2000,"Cisco2","Microsoft2");
Informatyk *ad = new Informatyk("Kowalski3",3000,"Cisco3","Microsoft3");
Ksiegowy *ae = new Ksiegowy("Kowalski4",3000,1);
Ksiegowy *af = new Ksiegowy("Kowalski5",3000,0);
lista_wsk[0]=*ab;
lista_wsk[1]=*ac;
lista_wsk[2]=*ad;
lista_wsk[3]=*ae;
lista_wsk[4]=*af;
for(int i;i<5;i++)
{
lista[i].info();
cout<<endl;
}
cout<<endl;
// for(int i;i<5;i++)
// {
// lista_wsk[i].info();
// }
return 0;
}
Ok and here goes my questions:
I had to create array which is filled with base class objects "Pracownik".
Secondary i had to create array which is full of pointers to class "Pracownik" objects.
(Hope those 2 first steps are done correctly)
Next thing I had to write to array 3 objects of class Informatic and 2 of class Accountant.
So I ve created 5 objects manually and added them into the array in such way array[0]=a;. I guess this is still good.
Next thing i had to create and add similar objects to array of pointers using new. So I ve created array with new and pointers to objects with new. (Hope thats correct 2).
And FINALLY:
I had to use info() on added to array objects.
This is my main question if my array is type "Pracownik" and I want to use function info() from subclasses how should I do that? And how compiler will know if he should use info() from Accountant or Informatic while I am trying to show those information using "for".
In an array of Pracownik, the elements are of type Pracownik. Any information about the objects being of a subclass of Pracownik are lost when you copy the elements into the array.
This is called object slicing and leads to the fact that there is no way to invoke Informatyk::info() on these objects.
If you want to call methods of a subclass, you have to prevent object slicing by storing pointers or references in the array.
As Oswald says in his answer,
Pracownik * lista_wsk = new Pracownik[10];
allocates an array of 10 Pracownik objects. This is probably not what you want. With polymorphism involved, we usually want to deal with pointers or references. Hence, you'd want an array of Pracownik * pointers. Since you already know at compile-time that it will have 10 members, there is no need for a dynamic allocation here. I think you've meant to write
Pracownik * lista_wsk[10];
instead. Now we don't put objects but pointers to objects into the array. For example:
lista_wsk[2] = new Informatyk("Kowalski3", 3000, "Cisco3", "Microsoft3");
And then we can iterate over the items like so:
for (unsigned i = 0; i < 10; ++i)
std::cout << lista_wsk[i]->getNazwisko() << std::endl;
As you have already discovered, it is impossible to call a subclass function member on a superclass object. It would be possible to figure out the actual type at run-time yourslf by means of a cast.
for (unsigned i = 0; i < 10; ++i)
if (Informatyk * info_ptr = dynamic_cast<Informatyk *>(lista_wsk[i]))
info_ptr->info();
dynamic_cast returns a pointer to the target class if this is possible or a nullptr (which evaluates to false, hence the conditional) otherwise. Note however that this is considered very poor style. It is better to use virtual functions. Therefore, add
virtual void
info()
{
// Do what is appropriate to do for a plain Pracownik.
// Maybe leave this function empty.
}
to the superclass and again to the subclass
virtual void
info() // override
{
// Do what is appropriate to do for an Informatyk.
}
The function in the subclass with the same signature is said to override the function inherited from the superclass. Since the function is marked as virtual, the compiler will generate additional code to figure out at run-time what version of the function to call.
If you are coding C++11, you can make the override explicit by placing the keyword override after its type as shown above (uncomment the override). I recommend you use this to avoid bugs that arise from accidental misspelling or other typos.
I have a pretty standard class with some public member functions and private variables.
My problem originally stems from not being able to dynamically name object instances of my class so I created an array of pointers of the class type:
static CShape* shapeDB[dbSize];
I have some prompts to get info for the fields to be passed to the constructor (this seems to work):
shapeDB[CShape::openSlot] = new CShape(iParam1,sParam1,sParam2);
openSlot increments properly so if I were to create another CShape object, it would have the next pointer pointing to it. This next bit of code doesn't work and crashes consistently:
cout << shapeDB[2]->getName() << " has a surface area of: " << shapeDB[2]->getSA() << shapeDB[2]->getUnits() << endl;
The array of pointers is declared globally outside of main and the get() functions are public within the class returning strings or integers. I'm not sure what I'm doing wrong but something relating to the pointer set up I'm sure. I'm writing this code to try and learn more about classes/pointers and have gotten seriously stumped as I can't find anyone else trying to do this.
I'm also curious as to what the CShape new instances get named..? if there is any other way to dynamically create object instances and track the names so as to be able to access them for member functions, I'm all ears.
I've tried all sorts of permutations of pointer referencing/de-referencing but most are unable to compile. I can post larger chunks or all of the code if anyone thinks that will help.
class CShape {
int dim[maxFaces];
int faces;
string units;
string type;
string name;
bool initialized;
int slot;
public:
static int openSlot;
CShape();
CShape(int, string, string); // faces, units, name
~CShape();
void initialize(void);
// external assist functions
int getA(void) {
return 0;
}
int getSA(void) {
int tempSA = 0;
// initialize if not
if(initialized == false) {
initialize();
}
// if initialized, calculate SA
if(initialized == true) {
for(int i = 0; i < faces; i++)
{
tempSA += dim[i];
}
return(tempSA);
}
return 0;
}
string getUnits(void) {
return(units);
}
string getName(void) {
return(name);
}
// friend functions
friend int printDetails(string);
};
// constructor with values
CShape::CShape(int f, string u, string n) {
initialized = false;
faces = f;
units = u;
name = n;
slot = openSlot;
openSlot++;
}
My guess is you use the CShape constructor to increment CShape::openSlot?
You're probably changing the value before it's read, thus the pointer is stored in a different location.
Try replacing openSlot with a fixed value to rule out this CShape::option.
-- code was added --
I'm pretty sure this is the problem, the constructor is executed before the asignment, which means the lhs. will be evaluated after CShape::openSlot is incremented.
I am working on a project that must allow all instances created to have a unique objID. All classes inherit from one base class that has a static variable that increments whenever any of the concrete classes constructor(s) are called. The counter keeps running until program is quit.
The problem I am having is when I use an array (any C++ containers), the objID registers more then one increment.
For example:
In my main() I created a vector<ConcreteClassA> cc; and did a push_back(...) twice.
My Output was:
objID = 5, and count = 2
Expected Result:
objID = 2, and count = 2
I am not sure why my ObjID is registering more then once for each push_back(...). I have gone through and checked all locations to make sure my assign() is only called in constructors of my concrete classes.
Please advise.
//===========================================================================
//Main.cpp
#include "ConcreteClassA.h";
#include <iostream>
#include <vector>
#using namespace std;
int main()
{
vector<ConcreteClassA> c1;
cc.push_back( ConcreteClassA() );
cc.push_back( ConcreteClassA() );
return 0;
}
//objID is off for some reason....
//Expected Results: count = 2, objID = 2
//Output: count = 2, objID = 5
//===========================================================================
//IBase.h file
private:
static int objID; //used to assign unique IDs
static int count; //track how many stances of all objs are active
protected:
const int assignID(); //return a new ID
void decrementCount(); //decrement count, when an obj is removed
//===========================================================================
//IBase.cpp
int IBase::objID = 0;
int IBase::count= 0;
const int IBase::assignID()
{
++ this->count;
++ this->objID;
return ( this->objID );
}
void IBase::decrementCount()
{
-- this->count;
}
//===========================================================================
ConcreteClassA.h
ConcreteClassA(); //default constructor
ConcreteClassA(...); //couple overloaded constructors
~ConcreteClassA(); //destructor
ConcreteClassA( const ConcreteClassA &cc ); //copy constructor
ConcreteClassA& operator=(const ConcreteClassA &cc); //assignment operator
//more methods....
//===========================================================================
ConcreteClassA.cpp
//destructor makes sure instances tracking counter is decremented
ConcreteClassA::~ConcreteClassA()
{
this->decrementCount();
}
//only constructors and assignemnt operators call assign() method
ConcreteClassA::ConcreteClassA()
{
//some other initialization...
this->assignID();
}
//All other methods implementation left out for sensitivity of real estate...
You have to account for copies of the object. In C++ vector<T>::push_back() puts a copy of the object into the vector. The temp instances that you created in the function call are getting destroyed. That is why the "created" count is higher that the "active" count.
If you really want to be stingy about creating instances of the object, maybe you should store pointers in the vector. That way you have to explicitly create and destroy them.
Here's a nice post on something like that:
https://stackoverflow.com/a/1361227/2174
void IBase::decrementCount()
{
-- this->count;
}
Woah, I haven't checked the operator precedence there, but I'd write
void IBase::decrementCount()
{
-- (this->count);
}
without even thinking twice. (A good guideline is, that if you can have a doubt or would want to check, you should write it more clearly).
And, yes, that should just be
void IBase::decrementCount()
{
--count;
}
I think the problem is in main() but this compiles fine but I get no output. I think maybe it's not initalizing correctly because in debug mode it says
"myCharQ {item=0x0018fa00 "ÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌÌ̺yâpú" front=-858993460 rear=-858993460 ...}"
How would you rewrite this so that it is proper? I'm just starting out with classes so any help would be useful.
The following is a Array based Queue class
#include <iostream>
#include <cstdlib>
using namespace std;
const int MaxQueueSize = 10; // Queue Struct can hold up to 10 char.
typedef char ItemType; // the queue's data type is char
class CPPQueue
{
public:
CPPQueue();
ItemType item[MaxQueueSize];
void initQueue(CPPQueue q);
bool IsEmpty(CPPQueue q);
bool IsFull(CPPQueue q);
void Enqueue(CPPQueue q, ItemType newItem);
void PrintQ(const CPPQueue q);
void PrintQueueInfo(CPPQueue myQ);
ItemType Dequeue(CPPQueue q);
private:
int front, rear;
int count;
};
CPPQueue::CPPQueue()
{
int front, rear, count = 0;
}
void CPPQueue::initQueue(CPPQueue q)
{
q.front = q.rear = q.count = 0;
}
bool CPPQueue::IsEmpty(CPPQueue q)
{
return (q.count == 0);
}
bool CPPQueue::IsFull(CPPQueue q)
{
return (q.count == MaxQueueSize);
}
void CPPQueue::Enqueue(CPPQueue q, ItemType newItem)
{
if(q.count == MaxQueueSize)
{
cerr << "Error! Queue is full, cannot enqueue item.\n" << endl;
exit(1);
}
q.item[q.rear] = newItem;
q.rear++;
if (q.rear == MaxQueueSize)
{
q.rear = 0; // adjustment for circular queue
}
q.count++;
}
ItemType CPPQueue::Dequeue(CPPQueue q)
{
ItemType theItem;
if(q.count == 0)
{
cerr << "Error! Queue is empty, cannot dequeue item.\n" << endl;
exit(1);
}
theItem = q.item[ q.front ];
q.front++;
if (q.front == MaxQueueSize)
{
q.front = 0; // adjustment for circular queue
}
q.count--;
return theItem;
}
// Function PrintQ() prints the contents of the queue without changing
// the queue. Printing starts at the "front" index and stops before we
// get to the "rear" index. A decrementing counter controls the loop.
//
void CPPQueue::PrintQ(const CPPQueue q)
{
int i;
int qindex = q.front;
for(i = q.count; i > 0; i--)
{
cout << q.item[qindex] ;
qindex = (++qindex) % MaxQueueSize; // adjustment for circular queue
if(i > 1)
cout << ", ";
}
}
// Helper function for the main program below.
void CPPQueue::PrintQueueInfo(CPPQueue myQ)
{
cout << "The queue contains: ";
PrintQ(myQ);
cout << endl;
}
int main()
{
CPPQueue myCharQ;// queue holds characters
char ch; // char dequeued
myCharQ.initQueue(myCharQ);
myCharQ.Enqueue(myCharQ, 'a'); myCharQ.PrintQueueInfo(myCharQ);
myCharQ.Enqueue(myCharQ, 'b'); myCharQ.PrintQueueInfo(myCharQ);
myCharQ.Enqueue(myCharQ, 'c'); myCharQ.PrintQueueInfo(myCharQ);
ch = myCharQ.Dequeue(myCharQ); myCharQ.PrintQueueInfo(myCharQ);
ch = myCharQ.Dequeue(myCharQ); myCharQ.PrintQueueInfo(myCharQ);
myCharQ.Enqueue(myCharQ, 'e');
myCharQ.Enqueue(myCharQ, 'f'); myCharQ.PrintQueueInfo(myCharQ);
myCharQ.Enqueue(myCharQ, 'g'); myCharQ.PrintQueueInfo(myCharQ);
cout << endl;
// print the dequeued characters
while(!myCharQ.IsEmpty(myCharQ))
{
ch = myCharQ.Dequeue(myCharQ);
cout << ch << " ";
}
cout << endl << endl;
return 0;
}
You never initialize the member variables front, rear, and count. You shadow them in your constructor by declaring variables with the same names again. Drop the int and just assign them (though this is not why the values aren't printed correctly, more on that in a bit). Actually, don't do that either; use an initializer list:
CPPQueue::CPPQueue()
: front(0), rear(0), count(0)
{ }
Also, why do you have an initQueue function? You already have a constructor, rely on that to initialize your instance(s) (this is not C!).
Next, functions like IsEmpty are non-static member functions, yet they don't operate on the current instance. Don't take a queue as a parameter, just return if the instance is empty, full, whatever. Your code would have to be used like this:
Queue q;
q.IsEmpty(q);
Just strange. All of your member functions operate this way. When you cann a member function an implicit pointer to the current instance is passed as a hidden parameter (this). Therefore, each time the function is called it operates within the context of the instance it was called upon. You don't need to take an instance as a parameter.
Also realize that all of your functions take their arguments by value. You are going to be creating copies of these queues like crazy. If you modify the argument it will not be seen by the caller. For example:
void CPPQueue::initQueue(CPPQueue q)
{
q.front = q.rear = q.count = 0;
}
That is essentially useless (aside from that fact that an initialize function is unnecessary). The changes to q.front, q.rear, and q.count will not be visible outside of that function as you are operating on a copy.
So even though your constructor is broken due to variable shadowing, this is why you still don't print what you expect to after calling initQueue. You are modifying a copy.
As for your implementation, it is not robust at all. You expose the underlying array to clients of your class. This is a bad idea. The items in a queue should not be directly accessible. What if I decide to muck with the array directly? Now all of your state variables are wrong. front, rear, and count are all potentially invalid as I have modified the state of the queue without going through any of your functions.
It's not even necessary; all I should be able to do is queue and dequeue items. That's it. That's what a queue does. It is not an array, if I want an array I will use one.
So, in summary, kudos on beginning to learn a relatively complex language. Keep at it and don't get discouraged, we all have to learn this stuff at some point.
EDIT: I have to run, but here is a quick rewrite of some of your class. I have removed your typedef for the item type. Why? It is unnecessary. You are not going to change it to another type per some platform or other environmental change, so the typedef only hurts the usability of your class. typedefs are good for things that may change (i.e., int32_t) for some environmental reason, but if they aren't helping you or clients of your code they are just one more thing to get in the way.
class CPPQueue
{
public:
CPPQueue();
bool IsEmpty() const;
bool IsFull() const;
void Enqueue(char newItem);
char Dequeue();
void PrintQ() const;
void PrintQueueInfo() const;
private:
char item[MaxQueueSize];
int front
int rear;
int count;
};
CPPQueue::CPPQueue()
: front(0), rear(0), count(0) { }
bool CPPQueue::IsEmpty() const
{
// you don't actually need the this pointer
// here, but I included it to make it clear
// that you are accessing the count variable
// for the current instance of a CPPQueue
return this->count == 0;
}
I hope this helps you rewrite the rest of your class, gotta go now. I added const in teh declaration of functions that should not mutate the internal state of a CPPQueue. Do a search for "const correctness" to get a better idea of why you would do such a thing. Good luck!
In your constructor:
int front, rear, count = 0;
is wrong. These are local variables that shadow your member variables.
You should use member initializers (with colon after your constructor name) instead.
Also note that you are passing by value all over the place - you probably want to pass by reference instead - look at each function parameter and ask yourself, "do I want a new copy of my parameter or do I want to refer to the same parameter (same memory location) that I passed in?"
CPPQueue::CPPQueue() :
front(0), rear(0), count(0)
{
}
Note: #OP this is elementary-level C++ - you need to read up and get your basics down or you will run into many, many much more difficult to fix problems further down the line.
takeaway.o: In function `takeaway':
project:145: undefined reference to `vtable for takeaway'
project:145: undefined reference to `vtable for takeaway'
takeaway.o: In function `~takeaway':
project:151: undefined reference to `vtable for takeaway'
project:151: undefined reference to `vtable for takeaway'
takeaway.o: In function `gameCore':
project.h:109: undefined reference to `gameCore<int>::initialData(int)'
collect2: ld returned 1 exit status
make: *** [takeaway] Error 1
I keep getting this Error from the linker , i know it has something to do with inline functions getting a vtable temporarily stored. But what that entails i am not quite sure. I would assume it has something to do with how i call gameCore's constructor in the initilization list of takeaway.cpp
I have a templated class (gameCore.h)
and a class (takeaway.cpp) that is inheriting from gameCore
The vtable error is called 3 times
1)in takeaways constructor
2) takeaways destructor
3)in gameCores constructor
I am using G++
Here is the code:
(i know it may seem hard to read but i have marked off exatcly where the erros occur)
takeaway.h
#ifndef _TAKEAWAY_H_
#define _TAKEAWAY_H_
#include<map>
#include<cctype>
#include<stack>
#include<map>
#include<iostream>
#include<string>
#include<cstdlib>
#include"gameCore.h"
#include<vector>
using namespace std;
class takeaway : public gameCore<int>
{
private:
public:
// template<class Penny>
void textualGame();
bool isNum(string str);
// template<class Penny>
stack<int> initialData(int initial);
// template<class Position>
int score (int position);
// template<class Position>
stack<int> addStack(int currentPos, stack<int> possiblePositions);
// template<class Penny>
takeaway (int initial);
// template<class Position>
~takeaway();
};
bool isNum(string str);
int charToint(char *theChar);
#endif
takeaway.cpp
/*
Description :
This game communicates with the gameCore class to determine the results
of a game of takeaway played between two computers or a computer and human.
*/
#include "takeaway.h"
/*
Description:Creates a stack represening initial data
Note:Change to a vector eventually
return : stack of int
*/
stack<int> takeaway:: initialData(int initial){
stack<int> returnStack;
int theScore = score(initial);
int final;
if(initial ==0)
{
final = 1;
}
else
{
final = 0;
}
returnStack.push(theScore);
returnStack.push(final);
return returnStack;
}
/*
Description: a textual representation of the game
Note: This is still terribly wrong
*/
void textualGame(){
cout <<"this is the best i could do for a graphical representation";
}
/*
Description: Deetermines if a number is even
Note: Helper function for determining win or loss positions
Returns: 1 if it is and 0 if it is not
*/
int takeaway::score(int position){
if(position % 2 == 0)
{
return 1;
}
return 0;
}
/*
Description: Will return a stack , withouth the given postion in it
will contain all positions possible after the given position
along with anyother that wehre in the given stack.This function
Must also update the map to represent updated positions
Takes: a position to check and a stack to return
Returns: A stack of possible positions.
*/
stack<int> takeaway::addStack(int currentPos, stack<int> possiblePositions ){
if(currentPos != 0)
{
// If even
if( currentPos % 2 == 0)
{
// Create a data aray with score of the new positon and mark it as not final
int data[] = {score(currentPos/2),0};
vector<int> theData(data, data+sizeof(data));
int pos = currentPos/2;
// Add it to the map
//this -> gamesMap[currentPos/2] = dataArray;
this -> gamesMap.insert(std::pair<int, vector<int> >(pos, theData));
// Add it to the possible positions
possiblePositions.push(pos);
}
if(currentPos % 3 == 0)
{
int data[] = {score(currentPos/3),0};
vector<int> theData(data,data+sizeof(data));
int pos = currentPos/3;
//this -> gamesMap[currentPos/3] = dataArray;
this -> gamesMap.insert(std::pair<int, vector<int> >(pos, theData));
possiblePositions.push(pos);
}
// Work for the position that represents taking one penny
int minusFinal = 0;
if(currentPos - 1 == 0)
{
minusFinal = 1;
}
int data[] = {score(currentPos - 1),minusFinal};
vector<int> theData(data,data+sizeof(data));
int pos = currentPos - 1;
// this -> gamesMap[currentPos -1] = dataArary
this->gamesMap.insert(std::pair<int,vector<int> >(pos, theData));
possiblePositions.push(pos);
}
return possiblePositions;
}
/*
Description: Constructor for the takeaway game
OA takes: a initial position, and initial data for it
*/
takeaway::takeaway(int initial):gameCore<int>::gameCore(initial){ //<--- ERROR HERE
//Constructor
}
/*
Description: Destuctor
*/
takeaway::~takeaway(){ // <--------------------- ERROR HERE
//Destructor
}
//checks input and creates game.
int main(int argc, char* argv[]){
int numberPennies ;
string game = argv[0];
if(argc == 2 && isNum(argv[1]) )
{
int pennies = charToint(argv[1]);
takeaway gameInstance(pennies ); // Creates a instance of $
}
// else if(argc == 3 && argv[1] == "play" && isNum(argv[2]) )
// {
// int pennies = charToint(argv[2]);
// takeaway<int> gameInstance(pennies); // Craete a human playab$
// }
else
{
cerr << "Error->Usage: " << game <<" [play] numberOfPennies \n";
exit (1);
}
return 0;
}
//Converts a char to a integer
int charToint(char *theChar){
int theInt = atoi(theChar);
return theInt;
}
//Determines if a string is numeric
bool isNum(string str){
for(int i = 0;i < str.length() ;i++){
if(isdigit(str[i]) != 1)
{
cerr << "Error->Input: Number must be a Positive Integer the charecter '" << str[i]<< "' invalidated your input. \n" ;
exit(1);
return false;
}
}
return true;
}
gameCore.h
/*
gameCore.h
Description:
This class created gameMap that are written as a template
They will communicate with the specific game and the algorithm
To keep track of positions ans there values.
*/
#ifndef GAMECORE_H
#define GAMECORE_H
#include <map>
#include <stack>
#include <string>
#include <vector>
using namespace std;
template <class Position>
class gameCore
{
protected:
//Best Move used by algorithim
Position bestMove;
//The current highest score used by the algorithim
int highestScore ;
//Stack to be used to remmeber what move created the score
stack<Position> movedFrom;
//Stack used for the algorithim.
stack<Position> curWorkingPos;
//The actual Map that the data will be held in.
map<Position,vector<int> > gamesMap;
public:
/*
Description : finds the data array for a poisition
takes: a Position
Returns: a array of integers /**
*/
virtual stack<int> initialData(Position pos) = 0;
/*
Description: Game must implement a way to determine a positions
score.
*/
virtual int score(Position pos) = 0;
/*
Description: A Graphical representation of the game
*/
virtual void textualGame() = 0;
/*
Description: a virtual function implemented by the child class
it will return a stack without the given position in it.This stack
will contain all positions available from the given postion as well as
all position already in the given stack. Also it will update the map with
all generated positions.
TAkes: a postion to check and a stack of currently working positons.
*/
virtual stack<Position> addStack(Position currentPos, stack<Position> possiblePositions ) = 0;
/*
Description:Constructor that
Creates a Map with positions as the key.
And an array of two integers that represent the positions
value and if we have moved here in the past.
Takes: a Initial Position and a Array of integers
*/
gameCore(Position initial){ // <-----ERROR HERE
//Determine the initial data and add it to the map and queue.
stack<int> theData = initialData(initial);
int first = theData.top();
theData.pop();
int second = theData.top();
theData.pop();
int initialData[] = {first,second};
vector<int> posData(initialData,initialData+sizeof(initialData));
gamesMap[initial] = posData;
curWorkingPos.push(initial);
}
/*
Description:
A destructor for the class
*/
~gameCore(){
//I do nothing but , this class needs a destructor
}
/*
Description: Takes the current position and returns
that positions Score.
Takes: A position
Returns:A integer that is a positions score.
*/
int getPosScore(Position thePos) const {
return this ->gamesMap.find(thePos)->second[0];
}
/*
Description: Adds values to a stack based on the current position
Takes: a poistion
*/
void updateStack(Position curPos){
this ->curWorkingPos =addStack(curPos,this ->curWorkingPos ); // get a stack from the game
// The game has a function that takes a position and a stack and based on the positions returns a stack identical to the last but with added values that represent valid moves from the postion./
}
/*
Description : Takes a positions and returns a integer
that depends on if the position is a final pos or not
Takes: A position
Returns: A Bool that represents if the position is a final(1) or not (0).
*/
// Possible change
bool isFinal(Position thePos) {
typename map<Position,vector<int> >::iterator iter = this ->gamesMap.find(thePos);
return iter->second[1] == 1 ;
}
/*
Description: Based on the given position determine if a move needs to be made.
(if not this is a end game position and it will return itself) If a move needs
to be made it will return the position to move to that is ideal.
Note: (because all positions can be represented as integers for any game , the return
type is a integer)
*/
int evaluatePosition(Position possiblePosition ){
if(isFinal(possiblePosition)) //If this is a final position
{
return getPosScore(possiblePosition); //Return the score
}
else
{
updateStack(possiblePosition); //Put all possible positions from this in thte stack
while(this -> curWorkingPos.size() != 0)
{
this -> movedFrom.push(this->curWorkingPos.front()); //take the top of the possible positions stack and set it the the moved from stack
this -> curWorkingPos.pop();
int curScore = evaluatePosition(this ->movedFrom.top()); //Recursive call for school
curScore = curScore * -1; //Negate the score
if(curScore > this -> highestScore) // if the score resulting from this position is biggest seen
{
highestScore = curScore;
this ->movedFrom.pop(); //do this first to get rid of the the lowest point
this -> bestMove = this ->movedFrom.top(); // mark where the lowest point came from
}
else
{
this -> movedFrom.pop();
}
}
}
return this -> bestMove;
}
//A Structure to determine if a position has a lower value than the second
struct posCompare{
bool operator() (Position pos1,Position pos2) const {
return (pos1.getPosScore() < pos2.getPosScore());
}
};
};
#endif
One or more of your .cpp files is not being linked in, or some non-inline functions in some class are not defined. In particular, takeaway::textualGame()'s implementation can't be found. Note that you've defined a textualGame() at toplevel, but this is distinct from a takeaway::textualGame() implementation - probably you just forgot the takeaway:: there.
What the error means is that the linker can't find the "vtable" for a class - every class with virtual functions has a "vtable" data structure associated with it. In GCC, this vtable is generated in the same .cpp file as the first listed non-inline member of the class; if there's no non-inline members, it will be generated wherever you instantiate the class, I believe. So you're probably failing to link the .cpp file with that first-listed non-inline member, or never defining that member in the first place.
The first set of errors, for the missing vtable, are caused because you do not implement takeaway::textualGame(); instead you implement a non-member function, textualGame(). I think that adding the missing takeaway:: will fix that.
The cause of the last error is that you're calling a virtual function, initialData(), from the constructor of gameCore. At this stage, virtual functions are dispatched according to the type currently being constructed (gameCore), not the most derived class (takeaway). This particular function is pure virtual, and so calling it here gives undefined behaviour.
Two possible solutions:
Move the initialisation code for gameCore out of the constructor and into a separate initialisation function, which must be called after the object is fully constructed; or
Separate gameCore into two classes: an abstract interface to be implemented by takeaway, and a concrete class containing the state. Construct takeaway first, and then pass it (via a reference to the interface class) to the constructor of the concrete class.
I would recommend the second, as it is a move towards smaller classes and looser coupling, and it will be harder to use the classes incorrectly. The first is more error-prone, as there is no way be sure that the initialisation function is called correctly.
One final point: the destructor of a base class should usually either be virtual (to allow polymorphic deletion) or protected (to prevent invalid polymorphic deletion).
If a class defines virtual methods outside that class, then g++ generates the vtable only in the object file that contains the outside-of-class definition of the virtual method that was declared first:
//test.h
struct str
{
virtual void f();
virtual void g();
};
//test1.cpp
#include "test.h"
void str::f(){}
//test2.cpp
#include "test.h"
void str::g(){}
The vtable will be in test1.o, but not in test2.o
This is an optimisation g++ implements to avoid having to compile in-class-defined virtual methods that would get pulled in by the vtable.
The link error you describe suggests that the definition of a virtual method (str::f in the example above) is missing in your project.
You may take a look at this answer to an identical question (as I understand):
https://stackoverflow.com/a/1478553
The link posted there explains the problem.
For quick solving your problem you should try to code something like this:
ImplementingClass::virtualFunctionToImplement(){...}
It helped me a lot.
Missing implementation of a function in class
The reason I faced this issue was because I had deleted the function's implementation from the cpp file, but forgotten to delete the declaration from the .h file.
My answer doesn't specifically answer your question, but lets people who come to this thread looking for answer know that this can also one cause.
it suggests that you fail to link the explicitly instantiated basetype public gameCore (whereas the header file forward declares it).
Since we know nothing about your build config/library dependencies, we can't really tell which link flags/source files are missing, but I hope the hint alone helps you fix ti.
GNU linker, in my case companion of GCC 8.1.0, well detects not re-declared pure virtual methods, but above certain complexity of class design it fails to identify missing implementation of methods and answers with a flat "V-Table Missing",
or even tends to report missing implementation, in spite it is there.
The only solution then is to verify consistency of declaration of implementation manually, method by method.
if you have virutal deconstruct function, you need to write it like this: ~SubListener() override = default; , don't forget this =default