I keep looking around fruitlessly for the solution to this,
I have a number of classes which inherit from one base class:
#ifndef navalVesselClass
#define navalVesselClass
#include <iostream>
#include <string>
class navalVessel
{
public:
std::string Name_;
std::string Type_;
std::string OperatingCountry_;
std::string Built_;
std::string ServiceDate_;
bool Active_;
double Length_;
double Displacement_;
double Beam_;
double Draft_;
double Speed_;
double Range_;
private:
};
#endif
and then, for instance a class that inherits it:
#ifndef destroyerClass
#define destroyerClass
#include "surfaceCombatant.h"
#include <string>
class destroyer: public surfaceCombatant
{
public:
enum class ArmamentPrimary { ANTIAIR, MISSILE };
enum class ArmamentSecondary { TORPEDOS, MISSILE, ANTIAIR, ANTIGROUND };
ArmamentPrimary primaryArmament;
ArmamentSecondary secondaryArmament;
private:
};
#endif
Now, When i want to STORE these objects in a vector i'm creating a vector as follows
std::vector<navalVessel *> shipFleet
Using that, i can store both, destroyers and other ships in this vector as pointers, HOWEVER once i try and retrieve them again, they are of course of the type 'navalVessel' and i cannot access any of the derived classes variables? e.g. primary weapon, i can only get access to the base class attributes.
Thanks.
Your design is flawed and you won't be able to do this without enabling RTTI and using dynamic_cast.
But this is useless as your design would remain bad or getting even worse.
I would suggest reading on some OOP basics as you do not fully understand the implication of base classes and polymorphism.
The idea of a base class is to provide a common set of methods which results in different values depending upon implementation of a concrete type.
In your scenario your base class should provide a member to obtain a list of possible weapons and every concrete implementation would return their set of weapons. Ships without weapons would return an empty list, etc.
But again. You have deep flaws in your understanding of abstract classes in C++. It is absolutely necessary to provide at least a virtual destructor in your base class.
Cast navalVessel* to pointer of whatever derived class the actual type is. Use a dynamic_cast.
Related
Having spent quite some time developping in C#, I noticed that if you declare an abstract class for the purpose of using it as an interface you cannot instantiate a vector of this abstract class to store instances of the children classes.
#pragma once
#include <iostream>
#include <vector>
using namespace std;
class IFunnyInterface
{
public:
virtual void IamFunny() = 0;
};
class FunnyImpl: IFunnyInterface
{
public:
virtual void IamFunny()
{
cout << "<INSERT JOKE HERE>";
}
};
class FunnyContainer
{
private:
std::vector <IFunnyInterface> funnyItems;
};
The line declaring the vector of abstract class causes this error in MS VS2005:
error C2259: 'IFunnyInterface' : cannot instantiate abstract class
I see an obvious workaround, which is to replace IFunnyInterface with the following:
class IFunnyInterface
{
public:
virtual void IamFunny()
{
throw new std::exception("not implemented");
}
};
Is this an acceptable workaround C++ wise ?
If not, is there any third party library like boost which could help me to get around this ?
Thank you for reading this !
Anthony
You can't instantiate abstract classes, thus a vector of abstract classes can't work.
You can however use a vector of pointers to abstract classes:
std::vector<IFunnyInterface*> ifVec;
This also allows you to actually use polymorphic behaviour - even if the class wasn't abstract, storing by value would lead to the problem of object slicing.
You can't create a vector of an abstract class type because you cannot create instances of an abstract class, and C++ Standard Library containers like std::vector store values (i.e. instances). If you want to do this, you will have to create a vector of pointers to the abstract class type.
Your workround would not work because virtual functions (which is why you want the abstract class in the first place) only work when called through pointers or references. You cannot create vectors of references either, so this is a second reason why you must use a vector of pointers.
You should realise that C++ and C# have very little in common. If you are intending to learn C++, you should think of it as starting from scratch, and read a good dedicated C++ tutorial such as Accelerated C++ by Koenig and Moo.
In this case we can't use even this code:
std::vector <IFunnyInterface*> funnyItems;
or
std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;
Because there is no IS A relationship between FunnyImpl and IFunnyInterface and there is no implicit convertion between FUnnyImpl and IFunnyInterface because of private inheritance.
You should update your code as follows:
class IFunnyInterface
{
public:
virtual void IamFunny() = 0;
};
class FunnyImpl: public IFunnyInterface
{
public:
virtual void IamFunny()
{
cout << "<INSERT JOKE HERE>";
}
};
The traditional alternative is to use a vector of pointers, like already noted.
For those who appreciate, Boost comes with a very interesting library: Pointer Containers which is perfectly suited for the task and frees you from the various problems implied by pointers:
lifetime management
double dereferencing of iterators
Note that this is significantly better than a vector of smart pointers, both in terms of performance and interface.
Now, there is a 3rd alternative, which is to change your hierarchy. For better insulation of the user, I have seen a number of times the following pattern used:
class IClass;
class MyClass
{
public:
typedef enum { Var1, Var2 } Type;
explicit MyClass(Type type);
int foo();
int bar();
private:
IClass* m_impl;
};
struct IClass
{
virtual ~IClass();
virtual int foo();
virtual int bar();
};
class MyClass1: public IClass { .. };
class MyClass2: public IClass { .. };
This is quite straightforward, and a variation of the Pimpl idiom enriched by a Strategy pattern.
It works, of course, only in the case where you do not wish to manipulate the "true" objects directly, and involves deep-copy. So it may not be what you wish.
Because to resize a vector you need to use the default constructor and the size of the class, which in turn requires it to be concrete.
You can use a pointer as other suggested.
std::vector will try to allocate memory to contain your type. If your class is purely virtual, the vector cannot know the size of the class it will have to allocate.
I think that with your workaround, you will be able to compile a vector<IFunnyInterface> but you won't be able to manipulate FunnyImpl inside of it. For example if IFunnyInterface (abstract class) is of size 20 (i dont really know) and FunnyImpl is of size 30 because it has more members and code, you will end up trying to fit 30 into your vector of 20
The solution would be to allocate memory on the heap with "new" and store pointers in vector<IFunnyInterface*>
I think that the root cause of this really sad limitation is the fact that constructors can not virtual. Thereof compiler can not generate code which copy the object without knowing its time in the compile time.
Having spent quite some time developping in C#, I noticed that if you declare an abstract class for the purpose of using it as an interface you cannot instantiate a vector of this abstract class to store instances of the children classes.
#pragma once
#include <iostream>
#include <vector>
using namespace std;
class IFunnyInterface
{
public:
virtual void IamFunny() = 0;
};
class FunnyImpl: IFunnyInterface
{
public:
virtual void IamFunny()
{
cout << "<INSERT JOKE HERE>";
}
};
class FunnyContainer
{
private:
std::vector <IFunnyInterface> funnyItems;
};
The line declaring the vector of abstract class causes this error in MS VS2005:
error C2259: 'IFunnyInterface' : cannot instantiate abstract class
I see an obvious workaround, which is to replace IFunnyInterface with the following:
class IFunnyInterface
{
public:
virtual void IamFunny()
{
throw new std::exception("not implemented");
}
};
Is this an acceptable workaround C++ wise ?
If not, is there any third party library like boost which could help me to get around this ?
Thank you for reading this !
Anthony
You can't instantiate abstract classes, thus a vector of abstract classes can't work.
You can however use a vector of pointers to abstract classes:
std::vector<IFunnyInterface*> ifVec;
This also allows you to actually use polymorphic behaviour - even if the class wasn't abstract, storing by value would lead to the problem of object slicing.
You can't create a vector of an abstract class type because you cannot create instances of an abstract class, and C++ Standard Library containers like std::vector store values (i.e. instances). If you want to do this, you will have to create a vector of pointers to the abstract class type.
Your workround would not work because virtual functions (which is why you want the abstract class in the first place) only work when called through pointers or references. You cannot create vectors of references either, so this is a second reason why you must use a vector of pointers.
You should realise that C++ and C# have very little in common. If you are intending to learn C++, you should think of it as starting from scratch, and read a good dedicated C++ tutorial such as Accelerated C++ by Koenig and Moo.
In this case we can't use even this code:
std::vector <IFunnyInterface*> funnyItems;
or
std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;
Because there is no IS A relationship between FunnyImpl and IFunnyInterface and there is no implicit convertion between FUnnyImpl and IFunnyInterface because of private inheritance.
You should update your code as follows:
class IFunnyInterface
{
public:
virtual void IamFunny() = 0;
};
class FunnyImpl: public IFunnyInterface
{
public:
virtual void IamFunny()
{
cout << "<INSERT JOKE HERE>";
}
};
The traditional alternative is to use a vector of pointers, like already noted.
For those who appreciate, Boost comes with a very interesting library: Pointer Containers which is perfectly suited for the task and frees you from the various problems implied by pointers:
lifetime management
double dereferencing of iterators
Note that this is significantly better than a vector of smart pointers, both in terms of performance and interface.
Now, there is a 3rd alternative, which is to change your hierarchy. For better insulation of the user, I have seen a number of times the following pattern used:
class IClass;
class MyClass
{
public:
typedef enum { Var1, Var2 } Type;
explicit MyClass(Type type);
int foo();
int bar();
private:
IClass* m_impl;
};
struct IClass
{
virtual ~IClass();
virtual int foo();
virtual int bar();
};
class MyClass1: public IClass { .. };
class MyClass2: public IClass { .. };
This is quite straightforward, and a variation of the Pimpl idiom enriched by a Strategy pattern.
It works, of course, only in the case where you do not wish to manipulate the "true" objects directly, and involves deep-copy. So it may not be what you wish.
Because to resize a vector you need to use the default constructor and the size of the class, which in turn requires it to be concrete.
You can use a pointer as other suggested.
std::vector will try to allocate memory to contain your type. If your class is purely virtual, the vector cannot know the size of the class it will have to allocate.
I think that with your workaround, you will be able to compile a vector<IFunnyInterface> but you won't be able to manipulate FunnyImpl inside of it. For example if IFunnyInterface (abstract class) is of size 20 (i dont really know) and FunnyImpl is of size 30 because it has more members and code, you will end up trying to fit 30 into your vector of 20
The solution would be to allocate memory on the heap with "new" and store pointers in vector<IFunnyInterface*>
I think that the root cause of this really sad limitation is the fact that constructors can not virtual. Thereof compiler can not generate code which copy the object without knowing its time in the compile time.
Having spent quite some time developping in C#, I noticed that if you declare an abstract class for the purpose of using it as an interface you cannot instantiate a vector of this abstract class to store instances of the children classes.
#pragma once
#include <iostream>
#include <vector>
using namespace std;
class IFunnyInterface
{
public:
virtual void IamFunny() = 0;
};
class FunnyImpl: IFunnyInterface
{
public:
virtual void IamFunny()
{
cout << "<INSERT JOKE HERE>";
}
};
class FunnyContainer
{
private:
std::vector <IFunnyInterface> funnyItems;
};
The line declaring the vector of abstract class causes this error in MS VS2005:
error C2259: 'IFunnyInterface' : cannot instantiate abstract class
I see an obvious workaround, which is to replace IFunnyInterface with the following:
class IFunnyInterface
{
public:
virtual void IamFunny()
{
throw new std::exception("not implemented");
}
};
Is this an acceptable workaround C++ wise ?
If not, is there any third party library like boost which could help me to get around this ?
Thank you for reading this !
Anthony
You can't instantiate abstract classes, thus a vector of abstract classes can't work.
You can however use a vector of pointers to abstract classes:
std::vector<IFunnyInterface*> ifVec;
This also allows you to actually use polymorphic behaviour - even if the class wasn't abstract, storing by value would lead to the problem of object slicing.
You can't create a vector of an abstract class type because you cannot create instances of an abstract class, and C++ Standard Library containers like std::vector store values (i.e. instances). If you want to do this, you will have to create a vector of pointers to the abstract class type.
Your workround would not work because virtual functions (which is why you want the abstract class in the first place) only work when called through pointers or references. You cannot create vectors of references either, so this is a second reason why you must use a vector of pointers.
You should realise that C++ and C# have very little in common. If you are intending to learn C++, you should think of it as starting from scratch, and read a good dedicated C++ tutorial such as Accelerated C++ by Koenig and Moo.
In this case we can't use even this code:
std::vector <IFunnyInterface*> funnyItems;
or
std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;
Because there is no IS A relationship between FunnyImpl and IFunnyInterface and there is no implicit convertion between FUnnyImpl and IFunnyInterface because of private inheritance.
You should update your code as follows:
class IFunnyInterface
{
public:
virtual void IamFunny() = 0;
};
class FunnyImpl: public IFunnyInterface
{
public:
virtual void IamFunny()
{
cout << "<INSERT JOKE HERE>";
}
};
The traditional alternative is to use a vector of pointers, like already noted.
For those who appreciate, Boost comes with a very interesting library: Pointer Containers which is perfectly suited for the task and frees you from the various problems implied by pointers:
lifetime management
double dereferencing of iterators
Note that this is significantly better than a vector of smart pointers, both in terms of performance and interface.
Now, there is a 3rd alternative, which is to change your hierarchy. For better insulation of the user, I have seen a number of times the following pattern used:
class IClass;
class MyClass
{
public:
typedef enum { Var1, Var2 } Type;
explicit MyClass(Type type);
int foo();
int bar();
private:
IClass* m_impl;
};
struct IClass
{
virtual ~IClass();
virtual int foo();
virtual int bar();
};
class MyClass1: public IClass { .. };
class MyClass2: public IClass { .. };
This is quite straightforward, and a variation of the Pimpl idiom enriched by a Strategy pattern.
It works, of course, only in the case where you do not wish to manipulate the "true" objects directly, and involves deep-copy. So it may not be what you wish.
Because to resize a vector you need to use the default constructor and the size of the class, which in turn requires it to be concrete.
You can use a pointer as other suggested.
std::vector will try to allocate memory to contain your type. If your class is purely virtual, the vector cannot know the size of the class it will have to allocate.
I think that with your workaround, you will be able to compile a vector<IFunnyInterface> but you won't be able to manipulate FunnyImpl inside of it. For example if IFunnyInterface (abstract class) is of size 20 (i dont really know) and FunnyImpl is of size 30 because it has more members and code, you will end up trying to fit 30 into your vector of 20
The solution would be to allocate memory on the heap with "new" and store pointers in vector<IFunnyInterface*>
I think that the root cause of this really sad limitation is the fact that constructors can not virtual. Thereof compiler can not generate code which copy the object without knowing its time in the compile time.
for example. We have class A and its derived class; class B. Is it possible to instantiate a pointer of type B in class A?
#ifndef WARRIOR_H
#define WARRIOR_H
#include "CharacterPlayer.h"
class Warrior: public CharacterPlayer
{
public:
virtual void printCharacterName();
};
#endif
however, when i try to instantiate a Warrior pointer or try to include "Warrior.h", it gives me a number of syntax errors.
#ifndef CHARACTERPLAYER_H
#define CHARACTERPLAYER_H
#include "Warrior.h"
class CharacterPlayer
{
public:
Warrior *warriorPntr = nullptr;
virtual void printCharacterName();
};
#endif
You have a circular dependency among your headers: "CharacterPlayer.h" includes "Warrior.h", and "Warrior.h" includes "CharacterPlayer.h". This cannot compile, because the "sentinel" will stop inclusion.
The trick is to forward-declare the Warrior class in the CharacterPlayer instead of including a header:
#ifndef CHARACTERPLAYER_H
#define CHARACTERPLAYER_H
class Warrior; // <<== Here
class CharacterPlayer
{
public:
Warrior *warriorPntr = nullptr;
virtual void printCharacterName();
};
#endif
This is good enough to declare pointers and references to Warrior, but not enough to call methods or instantiate the class.
You need to eventually include the header for Warrior in the cpp file CharacterPlayer.cpp, but that would not lead to any issues, because there is no circular dependency among your headers.
You can, but it's not a good idea.
To do it, simply remove the "#include "Warrior.h" from your second header, and replace it with a declaration of the form "class Warrior;"
As to why it is a bad idea: making a base class contain a pointer to (or an instance of) a derived class is one way (of several) that ensures implementation of the base class depends on implementation of the derived class. Generally speaking, the definition of derived class should depend on the base class, not the reverse, in order to avoid circular dependencies, to allow the base class to be used polymorphically (look up "Liskov Substitution Principle), etc etc.
This means, although it can be done, your design is fundamentally flawed. It would prevent you doing other useful things in future.
So I have a vector of pointers to baseclass, which is used to hold all instances of derived classes.
Base class:
#ifndef BASE_H
#define BASE_H
Class Base
{
public:
virtual void DoSomething();
};
#endif
A derived class:
#ifndef DERIVED_H
#define DERIVED_H
#include "base.h"
Class Derived : public Base
{
public:
void DoSomething();
float y;
};
#endif
With these things being stored inside:
std::vector<Base*> theVec;
The question being, what is the best way to access the float variable "y" that exists only in Derived?
I could have a virtual function in Base that is specified in Derived to return a variable, where access looks like:
theVec[0]->GetVar("y");
but when Derived is likely to have multiple variables of different types that are not in base this seems like it will end up being quite messy. Are there any ways to make access to a unique variable in Derived more generic?
Any suggestions would be greatly appreciated!
You use dynamic_cast.
if (Derived* ptr = dynamic_cast<Derived*>(theVec[0])) {
// do something here
}
There is no elegant solution to this problem. If you have it, your design is probably flawed, and you should rethink it. Having special cases for sub-classes defeats the purpose of polymorphism (although I admit that sometimes it is the only solution).