Suppose I have a base class that is an abstract interface, and two derived classes, which inherit a certain state from the base class. I want to change which derived class I'm using at run-time, but I want to preserve the shared state.
class Base{
public:
virtual void abstract() = 0;
SharedState ss;
};
class Der1 : public Base{
Der1() = default;
virtual void abstract() {//bla bla};
Der1(SharedState &s){
ss = s;};
};
class Der2 : public Base{
Der2() = default;
virtual void abstract(){//bla bla 2};
Der2(SharedState &s){
ss = s;};
};
struct SharedState{
int x,y,z;
float x1,y1,z1;
//etc...
}
I my handler code, I have a smart pointer that changes behaviour based on class type at run-time, hence the shared state constructor.
//driver code
std::unique_ptr<Base> ptr = std::make_unique<Der1>();
I'm planning to change the type, but with such a constructor I can preserve the state. However it is highly annoying to preface every member of the shared state with ss., is there a way to avoid this, perhaps with a using declaration of some sort?
Edit: I know I can move the shared state in the base and make it static, but that leads to performance drops when I'm not using this interface.
This is an ugly answer, but is an answer, solves the "ss" problem and can be usefull.
I overloaded the operator [] to directly return the values of your struct
struct SharedState{
int x,y,z;
float x1,y1,z1;
//etc...
};
class Base{
public:
virtual void abstract() = 0;
SharedState ss;
public:
int& operator[](const std::string rhs)
{
if(rhs == "x") //Here you will manage all the struct members, probably a map
return this->ss.x; // return the result by reference
}
};
class Der1 : public Base{
void abstract() override { };
public:
Der1(SharedState &s){
ss = s;};
};
class Der2 : public Base{
void abstract() override { };
public:
Der2(SharedState &s){
ss = s;};
};
int main()
{
SharedState ss;
ss.x = 100;
std::unique_ptr<Base> ptr = std::make_unique<Der1>(ss);
std::cout << (*ptr)["x"] << std::endl;
(*ptr)["x"] = 5; // You can change it too
std::cout << (*ptr)["x"] << std::endl;
std::unique_ptr<Base> ptr2 = std::make_unique<Der2>(ptr->ss);
std::cout << (*ptr2)["x"] << std::endl;
}
Related
I want to make something functionally similar to this:
class Base
{
public:
const int ClassID = 1;
}
class Derived1 : public Base
{
public:
const int ClassID = 2;
}
class Derived2 : public Base
{
public:
const int ClassID = 3;
}
But, obviously, you can't override variables. What would be the best way to achieve the same functionality?
(The context for this is a video game where there are different troops, overridden from the same parent "BaseTroop" class. I want each troop to have its own ID that can be retrieved from anywhere)
Thanks in advance!
There is no way to change the default member initialiser in derived classes.
But, there is no need to rely on the default initialiser. You can provide an initialiser in the constructor:
struct Derived1 : Base
{
Derived1(): Base{2} {}
};
struct Derived2 : Base
{
Derived2(): Base{3} {}
};
Add a virtual function, returning the ID. You can also have a static variable or function returning the same ID, in case you want to get it without a class instance. You can also assign the IDs automatically, using CRTP:
#include <iostream>
struct BaseLow
{
virtual int GetId() const = 0;
virtual ~BaseLow() {}
};
namespace impl
{
int &GetIdCounter()
{
static int ret = 0;
return ret;
}
}
template <typename Derived>
struct Base : BaseLow
{
inline static const int id = impl::GetIdCounter()++;
int GetId() const override final
{
return id;
}
};
struct Derived1 : Base<Derived1> {};
struct Derived2 : Base<Derived2> {};
int main()
{
std::cout << Derived1::id << '\n'; // 0
std::cout << Derived2::id << '\n'; // 1
Derived1 d1;
Derived2 d2;
BaseLow *ptrs[] = {&d1, &d2};
for (BaseLow *ptr : ptrs)
std::cout << ptr->GetId() << '\n'; // 0, 1
}
I have a base class B with derived classes X, Y and Z (in fact, more than 20 derived classes). Each class has a tag() function that identifies which (derived) class it is. My program stores instances of the derived classes as pointers in a vector defined as vector<B*>. Each derived class may appear in this vector 0..n times.
I would like to have a function that looks through the vector for instances of a derived type and returns a new vector with the type of the derived class, eg
#include <vector>
using namespace std;
class B {
public:
// ...
virtual int tag() {return 0xFF;};
};
class X : public B {
// ...
int tag() {return 1;};
vector<X*> find_derived(vector<B*> base_vec) {
vector<X*> derived_vec;
for (auto p : base_vec) {
if (p->tag() == tag()) {
derived_vec.push_back((X*) p);
}
}
return derived_vec;
}
};
Obviously I don't want to have to define find_derived in each derived class but I don't see how to do this as a virtual function. Currently I am doing it using a macro but, since I am learning C++, I woudl prefer a method that used language constructs rather than those in the pre-processor. Is there another way?
One possibility:
template <typename D>
class FindDerivedMixin {
public:
vector<D*> find_derived(const vector<B*>& base_vec) {
int my_tag = static_cast<D*>(this)->tag();
vector<D*> derived_vec;
for (auto p : base_vec) {
if (p->tag() == my_tag) derived_vec.push_back(static_cast<D*>(p));
}
return derived_vec;
}
};
class X : public B, public FindDerivedMixin<X> {};
Like the previous answer, what you need is some template programming.
This is an example without mixin though:
#include <vector>
#include <iostream>
#include <type_traits>
#include <string>
//-----------------------------------------------------------------------------
// Base class
class Base
{
public:
virtual ~Base() = default;
// pure virtual method to be implemented by derived classes
virtual void Hello() const = 0;
protected:
// example of a constuctor with parameters
// it is protected since no instances of Base
// should be made by accident.
explicit Base(const std::string& message) :
m_message(message)
{
}
// getter for private member variable
const std::string& message() const
{
return m_message;
}
private:
std::string m_message;
};
//-----------------------------------------------------------------------------
// Class which contains a collection of derived classes of base
class Collection
{
public:
Collection() = default;
virtual ~Collection() = default;
// Add derived classes to the collection.
// Forward any arguments to the constructor of the derived class
template<typename type_t, typename... args_t>
void Add(args_t&&... args)
{
// compile time check if user adds a class that's derived from base.
static_assert(std::is_base_of_v<Base, type_t>,"You must add a class derived from Base");
// for polymorphism to work (casting) we need pointers to derived classes.
// use unique pointers to ensure it is the collection that will be the owner of the
// instances
m_collection.push_back(std::make_unique<type_t>(std::forward<args_t>(args)...));
}
// Getter function to get derived objects of type_t
template<typename type_t>
std::vector<type_t*> get_objects()
{
static_assert(std::is_base_of_v<Base, type_t>, "You must add a class derived from Base");
// return non-owning pointers to the derived classes
std::vector<type_t*> retval;
// loop over all objects in the collection of type std::unique_ptr<Base>
for (auto& ptr : m_collection)
{
// try to cast to a pointer to derived class of type_t
type_t* derived_ptr = dynamic_cast<type_t*>(ptr.get());
// if cast was succesful we have a pointer to the derived type
if (derived_ptr != nullptr)
{
// add the non-owning pointer to the vector that's going to be returned
retval.push_back(derived_ptr);
}
}
return retval;
}
private:
std::vector<std::unique_ptr<Base>> m_collection;
};
//-----------------------------------------------------------------------------
// some derived classes for testing.
class Derived1 :
public Base
{
public:
explicit Derived1(const std::string& message) :
Base(message)
{
}
virtual ~Derived1() = default;
void Hello() const override
{
std::cout << "Derived1 : " << message() << "\n";
}
};
//-----------------------------------------------------------------------------
class Derived2 :
public Base
{
public:
explicit Derived2(const std::string& message) :
Base(message)
{
}
virtual ~Derived2() = default;
void Hello() const override
{
std::cout << "Derived2 : " << message() << "\n";
}
};
//-----------------------------------------------------------------------------
int main()
{
Collection collection;
collection.Add<Derived1>("Instance 1");
collection.Add<Derived1>("Instance 2");
collection.Add<Derived2>("Instance 1");
collection.Add<Derived2>("Instance 2");
collection.Add<Derived1>("Instance 3");
// This is where template programming really helps
// the lines above where just to get the collection filled
auto objects = collection.get_objects<Derived1>();
for (auto& derived : objects)
{
derived->Hello();
}
return 0;
}
In the below code, I write a factory method to create objects of a type in a class heirachy.
#include <iostream>
#include <memory>
using namespace std;
enum Type {
_Base, _A, _B, _C
};
class Base{
private:
Type type = _Base;
public:
virtual Type getType(){
return type;
}};
class A : public Base{
private:
Type type = _A;
public:
using Base::Base;
};
class B : public Base{
private:
Type type = _B;
public:
using Base::Base;
};
class C : public Base{
private:
Type type = _C;
public:
using Base::Base;
};
shared_ptr<Base> letterFactory(Type which){
shared_ptr<Base> base = make_unique<Base>(Base());
switch (which){
case _A:
base = make_unique<Base>(A());
case _B:
base = make_unique<Base>(A());
case _C:
base = make_unique<Base>(C());
}
return base;
}
int main(){
shared_ptr<Base> instanceOfA = letterFactory(_A);
cout << instanceOfA->getType() << endl;
shared_ptr<Base> instanceOfB = letterFactory(_B);
cout << instanceOfB->getType() << endl;
shared_ptr<Base> instanceOfC = letterFactory(_C);
cout << instanceOfC->getType() << endl;
return 0;
};
The output is
0
0
0
How can I make the output
1
2
3
Your factory is a bit flawed and your getType() function is not overridden in the derived classes. I guess you wanted to do something along this lines:
#include <iostream>
#include <memory>
using namespace std;
enum Type {
_Base, _A, _B, _C
};
class Base{
public:
virtual ~Base() = default;
virtual Type getType() const {
return _Base;
};
};
class A : public Base{
public:
virtual Type getType() const override {
return _A;
};
};
class B : public Base{
public:
virtual Type getType() const override {
return _B;
};
};
class C : public Base{
public:
virtual Type getType() const override {
return _C;
};
};
unique_ptr<Base> letterFactory(Type which){
switch (which){
case _Base:
return make_unique<Base>();
case _A:
return make_unique<A>();
case _B:
return make_unique<B>();
case _C:
return make_unique<C>();
}
return nullptr;
}
int main(){
shared_ptr<Base> instanceOfA = letterFactory(_A);
cout << instanceOfA->getType() << endl;
shared_ptr<Base> instanceOfB = letterFactory(_B);
cout << instanceOfB->getType() << endl;
shared_ptr<Base> instanceOfC = letterFactory(_C);
cout << instanceOfC->getType() << endl;
return 0;
};
Notice that we got rid of the type member completely and instead properly overrode the getType() function. Furthermore, factory functions like this usually return unique_ptr (which can be implicitly converted to shared_ptr if you really want to).
Your Base class has a member type and a virtual member function getType() that returns the value of the member type. Your classes A, B, and C derive from Base. That means they all have a Base subobject. That subobject contains the member Base::type. In addition, they all also add another member type that is then not ever used by anything. Also, none of them override the getType method either. So whenever you call
instanceOfX->getType()
even if instanceOfX points to an instance of one of the derived classes, since none of the derived classes overrite getType, you'll end up calling Base::getType, which will return the value of Base::type, which is always _Base…
What you actually wanted was probably something along the lines of:
struct Base
{
virtual Type getType() const = 0;
protected:
Base() = default;
Base(Base&&) = default;
Base(const Base&) = default;
Base& operator =(Base&&) = default;
Base& operator =(const Base&) = default;
~Base() = default;
};
class A : public Base
{
public:
Type getType() const override { return _A; }
};
class B : public Base
{
public:
Type getType() const override { return _B; }
};
class C : public Base
{
public:
Type getType() const override { return _C; }
};
Note that this is almost certainly bad design. The only purpose such a getType method could serve is so that client code can find out the concrete type of the object that a Base* it got is pointing to. If you ever need this information, your design violates the Liskov Substitution principle…
Apart from all that, note that _Base, _A, _B, and _C are reserved names [lex.name]/3 that you're not supposed to use in C++ code…
I refactored your code a bit just to apply some of the best practices:
use enum class instead of the raw enum because enum class represents a scoped enumeration type and it is also strongly typed which means you cannot convert it that easily to integer as the raw enum (that's why we have to_integral template function)
prefer not to use using namespace std; because it imports the entirety of the std namespace into the current namespace of the program
also, I think you wanted to use std::make_shared instead of std::make_unique
#include <memory>
#include <iostream>
enum class Type : int {
_Base = 0,
_A = 1,
_B = 2,
_C = 3
};
class Base{
private:
Type type = Type::_Base;
public:
virtual Type getType(){
return type;
}
};
class A : public Base{
private:
Type type = Type::_A;
public:
virtual Type getType() override {
return type;
}
};
class B : public Base{
private:
Type type = Type::_B;
public:
virtual Type getType() override {
return type;
}
};
class C : public Base{
private:
Type type = Type::_C;
public:
virtual Type getType() override {
return type;
}
};
std::shared_ptr<Base> letterFactory(Type which){
switch (which){
case Type::_A:
return std::make_shared<A>();
case Type::_B:
return std::make_shared<B>();
case Type::_C:
return std::make_shared<C>();
default:
return std::make_shared<Base>(Base());
}
}
template <typename Enum>
constexpr typename std::enable_if<std::is_enum<Enum>::value,
typename std::underlying_type<Enum>::type>::type
to_integral(Enum const& value) {
return static_cast<typename std::underlying_type<Enum>::type>(value);
}
int main(){
std::shared_ptr<Base> instanceOfA = letterFactory(Type::_A);
std::cout << to_integral(instanceOfA->getType()) << std::endl;
std::shared_ptr<Base> instanceOfB = letterFactory(Type::_B);
std::cout << to_integral(instanceOfB->getType()) << std::endl;
std::shared_ptr<Base> instanceOfC = letterFactory(Type::_C);
std::cout << to_integral(instanceOfC->getType()) << std::endl;
return 0;
};
Live example
I am currently trying to switch the type of a derived class stored in a shared pointer to base class.
The problem is that the Derived class inherit from the Base class and is also templated as follow:
Base class:
#define PRINT(s) std::cout << s << std::endl
class Base {
public:
Base() : m_a(1) {}
virtual ~Base() = default;
virtual void print() { PRINT("BASE"); }
int m_a;
};
The derived class depend on an enumeration template:
enum eType { e0, e1 };
template<eType et>
class Derived : public Base { };
template<>
class Derived<e0> : public Base {
public:
Derived() { this->m_a = e0; }
void print() { PRINT("Derived e0, m_a value: " << e0 ); }
};
template<>
class Derived<e1> : public Base {
public:
Derived() { this->m_a = e1; }
void print() { PRINT("Derived e1, m_a value: " << e1 ); }
};
My objective is to have a shared pointer to the Base class so it would be possible to switch from the 2 derived classes as follow:
int main()
{
std::shared_ptr<Base> sp_00 = std::make_shared<Derived<e0>> ();
std::shared_ptr<Base> sp_01 = sp_00;
sp_01->print();
std::shared_ptr<Base> sp_10 = std::make_shared<Derived<e1>> ();
*sp_01 = *sp_10;
sp_01->print();
sp_10->print();
}
The only problem as on the line *sp_01 = *sp_10; I expect that the pointer to base class switch from the derived type Derived<e0> to the derived type Derived<e1>. However in my example I get a different result for the line sp_01->print(); and the line sp_10->print(); indicating that sp_01 stays as a Derived<e0> type.
I want to avoid sp_01 = sp_10; because it will change the pointer. In the above example, it would lead to sp_00 != sp_01 and I want both sp_00 and sp_01 to share the same object.
I tried to replace the template derived class by a non template derived class as follow:
class Derived_e0 : public Base {
public:
Derived() { this->m_a = e0; }
void print() { PRINT("Derived e0, m_a value: " << e0 ); }
};
class Derived_e1 : public Base {
public:
Derived() { this->m_a = e1; }
void print() { PRINT("Derived e1, m_a value: " << e1 ); }
};
and the following code give the same result as the one with template.
int main()
{
std::shared_ptr<Base> sp_00 = std::make_shared<Derived_e0> ();
std::shared_ptr<Base> sp_01 = sp_00;
sp_01->print();
std::shared_ptr<Base> sp_10 = std::make_shared<Derived_e1> ();
*sp_01 = *sp_10;
sp_01->print();
sp_10->print();
}
So my question is, how to switch the derived object pointed by a shared pointer without changing the shared_ptr itself (which is used in other part of the program ?)
Thanks, if you need any more information, please let me know
You cannot change the runtime type of sp_01 without re-assigning it because you cannot assign Derived<e1> to Derived<e0> (think of what would happen if these do not have the same size - you have allocated enough size for a Derived<e0>, not for a Derived<e1>!).
In my opinion, your design (or what you are trying to do with it) is flawed somewhere. However, if you really want to keep a "link" between sp_00 and sp_01, you probably need another "level" of pointer:
int main() {
std::shared_ptr<Base> *psp_01;
std::shared_ptr<Base> sp_00 = std::make_shared<Derived<e0>> ();
psp_01 = &sp_00;
(*psp_01)->print();
std::shared_ptr<Base> sp_10 = std::make_shared<Derived<e1>> ();
psp_01 = &sp_10;
(*psp_01)->print();
sp_10->print();
}
But again, I would analyze my design twice before using this.
You can dynamic_cast the raw pointer; something like
Derived* t = dymanic_cast<Derived*>(sp_00.get())
And you'll get NULL if it can't be cast, or a valid pointer if it can. That said, this requires RTTI to be built in, which will make your binary bigger, and needing to do this at all is a sign that your design isn't right.
I need a base class that gives me primitive type of data's pointer. I add a function in it. I derived types of class. I used void * to support all primitive types as a return type but it is like old C days. It is not good for OOP. Does one have an suggestion to do in a proper way in OOP?
#include <iostream>
class base {
public:
virtual void *getPtr() = 0;
virtual ~base() {};
};
class derivedAType : public base {
protected:
int _i;
public:
derivedAType(int i): _i(0) { _i = i; };
virtual ~derivedAType() {}
virtual void *getPtr() {
return static_cast<void *>(&_i);
}
};
class derivedBType : public base {
protected:
short _s;
public:
derivedBType(short s): _s(0) { _s = s; };
virtual ~derivedBType() {}
virtual void *getPtr() {
return static_cast<void *>(&_s);
}
};
int main()
{
base *b1 = new derivedAType(1203912);
base *b2 = new derivedBType(25273);
std::cout << "b1 : " << *(static_cast<int *>(b1->getPtr()))
<< "\nb2 : " << *(static_cast<short *>(b2->getPtr()))
<< std::endl;
delete b2;
delete b1;
return 0;
}
Make the base class a template class with the data type as the template variable
template<typename DataType>
class base {
virtual DataType* getPtr() = 0;
//...
};
and
class derivedAType : public base<int>
But this changes base class to a template class which means you cant store them together, base<int> is different from base<short>
If this isnt acceptable, the other options is just a tad bit cleaner than your code but abt the same, refer to this question. Basically derived class return types can reflect their true type and i think it should get automatically converted to void*, so you dont have to manually cast the pointer.
Not sure about your problem. But maybe a double callback can help:
class Callback {
public:
virtual void do_int( int i ) const = 0;
virtual void do_short( short s ) const = 0;
/* ... */
}
class base {
public:
virtual void do_stuff(const Callback & c); /* will need a more telling name */
virtual ~base() {};
};
class derivedAType : public base {
protected:
int _i;
public:
derivedAType(int i): _i(0) { _i = i; };
virtual ~derivedAType() {}
virtual void do_stuff(const Callback & c) {
c.do_int( _i );
}
};
class derivedBType : public base {
protected:
short _s;
public:
derivedBType(short s): _s(0) { _s = s; };
virtual ~derivedBType() {}
virtual void do_stuff( const Callback & c) {
c.do_short( _s );
}
};
class print_callback : public Callback {
public:
virtual void do_int( int i ) const { std::cout << i; }
virtual void do_short( short s ) const { std::cout << s; }
}
int main() {
base *b1 = new derivedAType(1203912);
base *b2 = new derivedBType(25273);
std::cout << "b1 : ";
b1->do_stuff(print_callback());
std::cout << "\nb2 : ";
b2->do_stuff(print_callback());
std::cout << std::endl;
delete b2;
delete b1;
return 0;
}
Of course you can simplify this by just storing the created print callback, and using it twice.