How can I have several inherited classes together in the same array? - c++

I have a few classes, ObjDef, PeopDef, NpcDef, and PlyDef, such that PlyDef and NpcDef each seperately inherit PeopDef, and PeopDef inherits ObjDef. Each class has functionality that builds on the class before it, so it's important that PeopDef::Tick is called before ObjDef::Tick. I have every object stored in a vector<ObjDef> object, but when the main tick loop goes through them, I want them to call the original classes' Tick, rather than ObjDef::Tick, which is what the vector<ObjDef> currently makes it do. Is there any way to do this, or do I have to have a separate vector for each class?

You can store an ObjDef pointer (ObjDef* or a smart pointer) in the vector and make the Tick method virtual.
Here's an example:
#include <iostream>
#include <vector>
#include <memory>
class ObjDef
{
public:
virtual void Tick()
{
std::cout << "ObjDef::Tick\n";
}
};
class PeopDef : public ObjDef
{
public:
virtual void Tick()
{
std::cout << "PeopDef::Tick\n";
}
};
int main()
{
std::vector<std::shared_ptr<ObjDef>> objects;
std::shared_ptr<ObjDef> obj(new ObjDef());
std::shared_ptr<ObjDef> peop(new PeopDef());
objects.push_back(obj);
objects.push_back(peop);
for (auto object : objects)
{
object->Tick();
}
return 0;
}

Related

Member values not accessed in vector of instances of different subclasses

Jumping off of the above question Making a vector of instances of different subclasses : when implementing a vector of (pointers to) different subclasses (initialized as vector<Base*> objects), I expect to be able to access the correct member variables based on the subclass called.
Below is sample code:
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class Entity {
public:
int index;
Entity() {};
virtual ~Entity() {};
virtual void hit() = 0;
};
class Mesh : public Entity {
public:
int index;
Mesh(int x) {this->index=x;};
virtual void hit() {}
};
int main() {
vector<unique_ptr<Entity>> objects;
objects.push_back(unique_ptr<Entity>(new Mesh(35)));
objects.push_back(unique_ptr<Entity>(new Mesh(10)));
for ( int i = 0 ; i < objects.size() ; i++ )
cout << objects[i]->index << endl;
return 0;
}
Where I expect
35
10
to be printed, meanwhile I get instead.
0
0
How can I access the correct member variable values in this scenario?
The problem is due to a misunderstanding on inheritance. If you redefine your Mesh as follows, it would work as expected:
class Mesh : public Entity {
public:
//int index; // No don't redefine: here you'd have two different indexes
Mesh(int x) {this->index=x;};
void hit() override {} // extraa safety: use override instead of virtual to be sure to override
};
Online demo
Not related to the problem, but some more thoughts:
You can of course only use the interface of the base class (here Entity).
It is not very prudent to expose the index member publicly. A safer option would be to have it private, and access it via a public getter. If the index should be set only at construction, the Entity constructor should take care of it, otherwise you could consider a public setter.

How using object pointer with dynamic array

Hello I'm studying c++ language and I'm really wondering that if use object Pointer with dynamic array. Weapon class is derived by CItem class. At this time I'm typing like this.
CItem* pItem = new cWeapon[m_size];
and I doing initialize each object like this
pItem[0].initialize();
pItem[1].initialize();
pItem[2].initialize();
pItem[3].initialize();
....
pItem[n].initialize();
However this time make problem. Size is different pItem and cWeapon. Because Pointer Operation cause error.
and I wondering that how solve this problem?
sorry about my fool English skill.
Example code:
#include <iostream>
#include <memory>
#include <vector>
class BaseItem // abstract class
{
public:
virtual void initialize() = 0; // pure virtual function (no implementation)
};
class Sword : public BaseItem
{
public:
void initialize() override
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
class Shield : public BaseItem
{
public:
void initialize() override
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
int main()
{
std::vector<std::unique_ptr<BaseItem>> items;
items.emplace_back(new Sword);
items.emplace_back(new Sword);
items.emplace_back(new Shield);
items.emplace_back(new Sword);
items.emplace_back(new Shield);
for(auto& element : items)
{
element->initialize();
}
return 0;
}
You can run it here: wandbox.org
Output:
virtual void Sword::initialize()
virtual void Sword::initialize()
virtual void Shield::initialize()
virtual void Sword::initialize()
virtual void Shield::initialize()
In this implementation I used std::vector for dynamic arrays. Vector is containing types of smart pointer to BaseItem. In this case smart pointer is std::unique_ptr it helps a lot with resource management and it is easy to use. Without it you need manually delete all elements from vector. I really recomend using it.
Our BaseItem now can provide "interface" that we want to implement in any other class. If you don't want to force class to implement such method just don't make it pure virtual (remove = 0 and add {} body of function)
More information about:
C++ Abstract Class
__PRETTY_FUNCTION__
C++ virtual functions
C++ inheritance
This is kind of "old" approach. You can read also about composition and entity system (ES).

How should I store templated functions in any container?

I have a templated Prob class that acts as a way to organize various programming problems from different problem sets. The template is the problem number. How would I go about storing different Prob objects in a vector or map?
Here is the class declaration:
template<int ProbNum>
class Prob
{
std::string
text(std::ostream& out)
{
out << "Prob" << ((ProbNum < 10) ? "0" : "") << ProbNum << ": ";
}
void solve(std::ostream& out);
};
So in other words if I want to declare an object for problem 1 of some problem set I would do
Prob<1> p1;
and then store that in a map or vector for later use so the user can call on it during runtime (since you cannot pass a runtime argument into a template).
Edit: I want to use this class as an abstract class for other Prob classes.
Edit2: Added more code for clarification.
Edit3:
Top half is Prob1.hpp
Bottom half is a driver file on how I want to use it.
#include <iostream>
#include "Prob.hpp"
template<>
void
Prob<1>::solve(std::ostream& out)
{
out << "solution to prob1";
}
/***************************************************/
#include <iostream>
#include <cstdlib>
#include "Prob.hpp"
// Finished Problems
#include "Prob1.hpp"
int
main(int argc, char* argv[])
{
Prob<1> p;
p.solve(std::cout);
}
Each instance of a template class constitutes a different type. Hence, containers like std::vector cannot hold Prob<ProbNum> for different values of ProbNum.
If you know at compile time the number of Prob<ProbNum> instances you want, and the corresponding values of the template parameter int ProbNum you could store everything into a tuple. For example:
auto mycollection = std::make_tuple(Prob<1>(), Prob<2>());
A more general solution could be to define an abstract base class for Prob. Then you can manage to store a vector of Prob<ProbNum> objects, with inhomogeneous values of int ProbNum, if you define a vector of pointers to the base class. For this to work you must provide the interface in the base class, i.e., every member of Prob<ProbNum> that you want to access through the vector of the base class, must be virtual and already declared in the base class.
Consider the following example:
#include <iostream>
#include <memory>
#include <vector>
struct base {
virtual void print() const = 0;
virtual ~base() = default;
};
template <int i>
struct derived : public base
{
virtual void print() const { std::cout << i << std::endl; }
};
int main()
{
std::vector<std::unique_ptr<base>> vec;
vec.emplace_back(new derived<1>());
vec.emplace_back(new derived<3>());
vec.emplace_back(new derived<5>());
for (auto& el : vec)
el->print();
return 0;
}
The variable vec is essentially a vector of pointers to objects of type derived<i>, with inhomogeneous values of i. Because base::print() is virtual, it correctly resolves to the corresponding method of the derived<i> class.
Notice that I used a smart pointer to avoid memory leaking.
Also, it is important to declare virtual the destructor of base, see the discussion Why should I declare a virtual destructor for an abstract class in C++?.

C++ - Call Child Method Overwrites in List of Parents [duplicate]

This question already has answers here:
What is object slicing?
(18 answers)
Closed 5 years ago.
Here's a sample program for what I'm talking about:
#include <iostream>
#include <list>
using namespace std;
class Thing {
public:
virtual void jump() { cout << "Called from Thing class" << endl; }
};
class Car: public Thing {
public:
virtual void jump() { cout << "Vroom vroom; imma car biotch" << endl; }
};
int main(int argc, char* argv[]) {
Car myCar;
list<Thing> myList;
myList.push_back(myCar);
std::list<Thing>::iterator iterator;
for (iterator = myList.begin(); iterator != myList.end(); ++iterator) {
iterator->jump();
}
return 0;
}
Output:
Called from Thing class
What I want to do is make a list of "Things". I want to be able to add instances of the child class from the Thing class; then I want to be able to call their overwritten function from an iterator.
The only issue is, even while using the "virtual" keyword, the iterator uses the Thing::jump function as opposed to the Car::jump function.
What can I do to make it not only work with Car, but all potential child classes from Thing, Car, etc. that overwrite the "jump" function?
In C++ values are what they say they are. You have a list of Thing not a list of Thing or derived types.
When you insert something into the list, a copy of it is created. In the case of inserting a derived type instance, this copies the Thing portion of the object, something known as object slicing (the Thing portion is sliced off).
There are a number of ways to store polymorphic data, but an instance of the base type is not one of them.
You could store std::unique_ptr<Base> in your list, for example. If you do so, ensure your base type has a virtual destructor to avoid undefined behaviour when theynare deleted by the default deleter.
#include <iostream>
#include <list>
#include <memory>
class Thing {
public:
virtual void jump() { std::cout << "Called from Thing class\n"; }
~Thing(){}
};
class Car: public Thing {
public:
virtual void jump() { std::cout << "Vroom vroom; I am a car\n"; }
};
then in main:
std::list<std::unique_ptr<Thing>> myList;
myList.push_back(std::make_unique<Car>());
for(auto&& ptr:myList){
ptr->jump();
}

task list in C++ (vector with more than one type)

My aim is to fill a list of task; each will be an object containing the description of the task. Let'say there will be only two type of tasks : file copy and repertory copy.
Since a vector cannot contain more than one type of objects, I though to create a generic task class and two classes that inheritate from that one.
Here is the code :
#include <iostream>
#include <deque>
#include <string>
using namespace std;
class GenericTask{
public :
string config;
GenericTask(string s){
config=s;
}
void run(){
cout<<"Running generic task" <<endl;
}
};
class FileCopyTask : public GenericTask{
public:
string filename;
FileCopyTask(string cf,string fn):GenericTask(cf)
{
filename=fn;
}
void run(){
cout<<"file :"<<filename<<endl;
}
};
class RepertoryCopyTask : public GenericTask{
public:
string repname;
RepertoryCopyTask(string cf,string rn):GenericTask(cf)
{
repname=rn;
}
void run(){
cout<<"repertory : "<<repname<<endl;
}
};
void run_next(deque<GenericTask> &task_list){
task_list.front().run();
task_list.pop_front();
}
int main()
{
RepertoryCopyTask rtask("configuration","/home");
FileCopyTask ftask( "configutation","gile.tex" );
deque<GenericTask> task_list;
task_list.push_back(rtask);
task_list.push_back(ftask);
run_next(task_list);
}
As it, it does not work because run_next expect a GenericTask and both rtask and ftask are treated as generic.
How should I do ?
I already tried to add template here and there, but ultimately it does not work because I need to know the type inside the deque before to "extract" something.
Can I consider this as an answer ?
Why not create objects of FileCopyTask and RepertoryCopyTask and save them as pointers to GenericTask? This way you can leverage the power of runtime polymorphism.
Like this:
int main()
{
std::unique_ptr<GenericTask> ftask = std::make_unique<FileCopyTask>("configutation","gile.tex");
std::unique_ptr<GenericTask> rtask = std::make_unique<FileCopyTask>("configuration","/home");
...
}
void run_next(deque<std::unique_ptr<GenericTask> > &task_list)
{
....
}
Also, do not forget to mark the run() method in class GenericTask as virtual. Also provide a virtual destructor.
I made some changes in your source. Defined your base fn as virtual and stored objects with pointers. You can check it below.
#include <iostream>
#include <deque>
#include <string>
using namespace std;
class GenericTask{
public :
string config;
GenericTask(string s){
config=s;
}
virtual void run(){
cout<<"Running generic task" <<endl;
}
};
class FileCopyTask : public GenericTask{
public:
string filename;
FileCopyTask(string cf,string fn):GenericTask(cf)
{
filename=fn;
}
void run(){
cout<<"file :"<<filename<<endl;
}
};
class RepertoryCopyTask : public GenericTask{
public:
string repname;
RepertoryCopyTask(string cf,string rn):GenericTask(cf)
{
repname=rn;
}
void run(){
cout<<"repertory : "<<repname<<endl;
}
};
void run_next(deque<GenericTask*> &task_list){
task_list.front()->run();
task_list.pop_front();
}
int main()
{
RepertoryCopyTask* rtask = new RepertoryCopyTask("configuration","/home");
FileCopyTask* ftask = new FileCopyTask( "configutation","gile.tex" );
deque<GenericTask*> task_list;
task_list.push_back(ftask);
task_list.push_back(rtask);
run_next(task_list);
}
How should I do ?
Consider these steps:
define GenericTask as a base class (add virtual destructor, make void run virtual)
override the run function in derived classes
store elements in the queue as std::unique_ptr, instead of "by value" to avoid the slicing problem.
I already tried to add template here and there, but ultimately it does not work because I need to know the type inside the deque before to "extract" something.
You can add a boost::variant as the value, allowing the storage of unrelated types.
Can I consider this [this=answer proposing boost::any as value type] as an answer ?
Yes. boost::variant would be similar (the difference is that boost::any supports setting any value; boost::variant only supports values of the types provided as variant arguments).
A classical case of virtual. The run methods need to be declared virtual s.t. you are actually calling RepertoryCopyTask::run() on an object of type GenericTask.
When done correctly,
FileCopyTask t("a", "b");
GenericTask & g = t;
g.run();
will call FileCopyTask::run instead of GenericTask::run (which it would in the original question).
When you did this, you can't store your FileCopyTasks and RepertoryCopyTask in a contaianer for GenericTask. This is because they might even have different size. To get around this, you should store unique_ptrs for them in some container, i.e.
std::vector<std::unique_ptr<GenericTask> > tasks;
This would be the correct way of solving your problem.