quick question about inheritance and static members

Started by
9 comments, last by Zahlman 16 years, 2 months ago
I'm trying to make a simple state machine that uses inheritance, more as a pratice to better understand virtual function and inheritance it's self. so I've thought of a class like this that would store other classes of its self and could push and pop classes from a static stack. Am I doing this right?

class CTask
{
public:

// my understanding is that if I declare it static only one instance of it 
// should ever be created, but all instances of the CTask should have access 
// to it?
     static std::vector<CTask*> m_tasks;

     virtual CTask() = 0;
     virtual ~CTask() = 0;
     virtual void Init() = 0;
     virtual void Update() = 0;
     virtual void Display() = 0;
     virtual void Free() = 0; 

     void Push(CTask*);
     void Pop(CTask*);
};

would that be the prefered way to do it, or should I make seperate classes for CTask and CTask_Stack?

[ dev journal ]
[ current projects' videos ]
[ Zolo Project ]
I'm not mean, I just like to get to the point.
Advertisement
You're right that all tasks, and subclasses thereof, would share the same m_tasks. But why should there be exactly one task container per executable process? Why is the Task class responsible for scheduling and managing Tasks as well as being those tasks? Think about exactly what the responsibility of CTask is, and exactly what the responsibility of other classes should be.
I havn't realy thought it out much, but what I was trying to accompolish was an underlining interface for a simple game that could run on it's own without having to use "switch case" statements wich always lead to continuos updateing everytime I add a new component of the game... ad for me things always get cluttered that way. I also wanted to be able to incorporate the same kind of system for a scene graph and GUI's (ofcourse they'd be they're own systems altogether but they'd be designed similar)

As for having only one CTask container, it made sense in my first draft, but now that you bring it up I can already see ways it might be better to have seperate containers. Such as freeing groups of tasks would be alot easier if the parent task that was closing them had them in it's container. Then again it'll probably already have knowledge of all the CTasks that it's created anyways. Or maybe having it's own container would allow branching... Cause I what to have a sort of parent->child->child relation of everything, so if one thing gets updated, then it updates all its childs, same when it get's freed it'll free all its childs aswell.

IDK?
[ dev journal ]
[ current projects' videos ]
[ Zolo Project ]
I'm not mean, I just like to get to the point.
I personally would create a separate manager class for the tasks. This will allow you to separate management of the tasks.

Also, this would also allow multiple managers, better encapsulation, cleaner code, and no need for static...

Also, I would need to discourage using Push() and Pop() for routine names as they do not create a coherent interface. Better names would be "Add", "AddTask"--as long as it is at the same abstraction level the rest of the interface is.
I little more tinkering around, and here is why I still don't understand why a seperate manager would be worth it.

As far as I know a seperate manager that controls the stack, adds and removes tasks from the stack would have to know about all the possible tasks that would be added to the stack in order to add them to the stack... right?

with the task it'self being the container it only needs to know about the types of tasks that it deals with.

or am I missing something?
[ dev journal ]
[ current projects' videos ]
[ Zolo Project ]
I'm not mean, I just like to get to the point.
Quote:
As far as I know a seperate manager that controls the stack, adds and removes tasks from the stack would have to know about all the possible tasks that would be added to the stack in order to add them to the stack... right?

Incorrect. This is where the beauty of inheritance comes in. All we need to do is have the TaskManager class store a list of CTask* pointers. We can use a simple cast to convert a superclass (Classes derived from CTask) to its base class and vice versa.

For example...
CTask* task = new CSomeClassThatDerivesFromCTask;

This pointer now points to a derived task class object. We can also call any derived class routines through this base pointer as well, so long as it is defined. Making a routine pure virtual forces this rule, as it will display an error if the routine is not defined in a derived class.

Because of this, all the task manager needs to work on is a CTask*, which can represent any type of task.
Well the way I see it is the tasks themselves are going to create the new tasks then pass this new class onto the manager... correct?

example;

CTask could have something in it's update that would would look like

*CTaskTemp = new CTask;

That's all dandy but how would the manager know this is happening in a way that it makes things clear, and seperated. It seems either way the manager and the task are going to be very intertwined anyways. Also usuing the seperate manager, how would I still incorporate branching?

hopefully this stays formatted enough
          Task01            |       -----------       |         |          Task02    Task03       |     Task04       |     Task05


The way I wanted, it would still be like a stack were everything gets called in order, but with branching I could seperate things into groups. This would allow for example, If I paused Task02, then Task04 and Task05 would puase aswell cause there parents were paused and not calling them, but Task03 would still be called and could branch off into seperate groups.
[ dev journal ]
[ current projects' videos ]
[ Zolo Project ]
I'm not mean, I just like to get to the point.
Quote:
That's all dandy but how would the manager know this is happening in a way that it makes things clear, and seperated.

Here is an example of what I mean. Please note that the following code is only for demonstration purposes, and has not been compiled nor tested, but hopefully contains no errors :)
/*** Base Task abstract object*/class Task {public:     virtual Task() = 0;     virtual ~Task() = 0;     virtual void Init() = 0;     virtual void Update() = 0;     virtual void Display() = 0;     virtual void Free() = 0; };/*** System Init Task*/class InitTask : public Task {public:   // these routines must be defined. If they are not,   // compiler will issue errors.     virtual InitTask();     virtual ~InitTask();     virtual void Init();     virtual void Update();     virtual void Display();     virtual void Free(); };/*** Task Manager*/class TaskManager {   std::vector <Task*> m_pTasks;public:   void AddTask (Task* pTask) {       if (pTask) {          m_pTasks.push_back (pTask);          pTask->Init ();       }   }}

Assuming the above is your code, we can do this to create a new task:
TaskManager* _manager=new TaskManager;// Creates new initialization task for the manager to use. Careful about// using this method_manager->AddTask (new InitTask);// Creates new initialization taskInitTask* newTask = new InitTask;_manager->AddTask (newTask);// Now, we can have the manager call each tasks virtual methods to set up each// task through its base Task* pointer. For example...// Update tasks_manager->Update (); // calls each tasks Update() method through base pointer.                     // This works thanks to C++'s vtable implementation


I hope this clarifies my idea a little bit :)
call me dense, but the _manager pointer would this be store in the task to be able to call AddTask()?

Also, would branching be possible with this setup?
[ dev journal ]
[ current projects' videos ]
[ Zolo Project ]
I'm not mean, I just like to get to the point.
Quote:
call me dense, but the _manager pointer would this be store in the task to be able to call AddTask()?

Thats not needed. The code I posted works do to the use of inheritance.

This topic is closed to new replies.

Advertisement