I have a parent class A and 2 derived class A1 and A2:
class A {
}
class A1 : public class A {}
class A2 : public class A {}
And in another class B I want to keep a collection, which is composed of objects A1 or A2.
class B {
vector<A1> _A1s;
vector<A2> _A2s;
}
Rather than keep 2 separate vectors A1s and A2s, is there a way to combine these 2 vectors? I thought about vector or vector, but either way may lost objects when A1s or A2s resize (I assume).
Any idea?
You can use a vector of smart pointers to base type
std::vector<std::shared_ptr<A>> a_ptr;
Note:
You need to add virtual destructor to A
class A {
public:
virtual ~A() {}
};
usage:
struct B {
public:
B() {
a_ptr.push_back(std::shared_ptr<A>(new A1));
a_ptr.push_back(std::shared_ptr<A>(new A2));
}
private:
std::vector<std::shared_ptr<A>> a_ptr;
};
int main(int argc, char* argv[])
{
B b;
return 0;
}
Also you miss ; after class definition and you have extra keyword class in front of A in below statement:
class A1 : public class A {};
should be:
class A1 : public A {};
You can use a vector of pointer
class B {
public:
std::vector<A*> _AXs;
};
It will accept both A1 and A2 pointer types.
Note that in this case, you should set the destructor of the A class virtual.
If you don't, when you try to delete a A* objet, the program can't guess what is the real type.
class A {
public:
virtual ~A() {}
};
You can use what the others say, plus a design pattern called visitor if you want to still be able to use the underlying A pointer as a A1 or A2. The visitor pattern is not really designed for that but it’s nice to use it that way.
Related
Say that I have a class A and a class B which further has a subclass B1. Now I want to assign a pointer of B1 to be a member of A. I could do that by simply heap allocating B1 and then using delete in the destructor of A as follows:
class B {
public:
B() { };
};
class B1 : public B {
public:
B1() { };
};
class A {
public:
B* b_instance;
A(B* b_instance){
this->b_instance = b_instance;
};
~A(){
delete this->b_instance;
};
};
int main() {
B* b_instance = new B1();
A a_instance(b_instance);
return 0;
};
Now I believe another approach (and one that is more favored?) would use std::unique_ptr, whereby a unique_ptr to B1 is given to A and thus, as a smart pointer, whenever A is destroyed B1 is automatically destroyed as well since there are no more references to it. My best guess is that it should look something like this:
#include <memory>
class B {
public:
B() { };
};
class B1 : public B {
public:
B1() { };
};
class A {
public:
std::unique_ptr<B> b_instance;
A(B& b_instance){
this->b_instance = std::make_unique<B>(b_instance);
};
~A(){ };
};
int main() {
B1 b_instance;
A a_instance(b_instance);
return 0;
};
Is the unique_ptr approach the same as the approach outlined above so long as you remember to include delete this->b_instance in the destructor of A? Or are there other benefits of the unique_ptr approach?
Is the unique_ptr approach the same as the approach outlined above so long as you remember to include delete this->b_instance in the destructor of A?
More or less.
Or are there other benefits of the unique_ptr approach?
Yes, you get automatic move semantics and copy semantics is turned off, greatly helping to prevent involuntary multiple owners of the same resource.
Based on what I’ve read, it seems the memory for derived objects is made sequentially with the base class and all its data made first and then immediately followed by the following classes down the inheritance tree. So if I make a base class pointer that is equal to a new derived class object, and then increment it by one(which will actually add the size of the base class to the address), then will I arrive at the derived class? If so, can I then access the derived class’s data in this way?
Yes, and no. In the very simplest case it will work in most cases:
class Base {
public:
int v;
};
class Derived : public Base {
public:
int b;
};
int main() {
Derived d;
Base* p = &d;
p++;
// these will match on all compilers I'm aware of
printf("%p %p\n", p, &d.b);
return 0;
}
For single inheritance, that is typically what you'll see from most compilers (although I'd be very worried actually relying on that in production code!)
However, sadly things aren't always that simple! In C++ we often have multiple inheritance, virtual inheritance, abstract base classes and all those bits of goodness. So here is a scenario where it would absolutely not work!
struct Animal {
virtual ~Animal() = default;
virtual void Eat() {}
int a;
};
struct Mammal: Animal {
virtual void Breathe() {}
int b;
};
struct WingedAnimal: Animal {
virtual void Flap() {}
int c;
};
// A bat is a winged mammal
struct Bat: Mammal, WingedAnimal {
int d;
};
There are however far safer approaches to handling upcasting (e.g. dynamic_cast, or your own RTTI system). You probably will want to be using those :)
I have a base class that holds a vector of pointers to derived classes and a virtual recursive method like this:
class Base
{
std::vector<Base*> vec;
public:
Base(std::vector<Base*> vec = {}) : vec {vec} {}
virtual ~Base() = default;
virtual void f() const
{
if (vec.size() == 0)
throw std::logic_error("Last children should implement f()")
for (auto * part : vec)
part->f();
}
}
The method f() is recursive and the last child of Base should override it. For example the following works
Where the first derived class is
class B : public Base
{
B() : Base() {}
virtual void f() const
{
std::cout << "In B\n";
}
}
class A : public Base
{
B b_,c_;
public:
A(B b, B c) : Base({&b_,&c_}), b_ {b}, c_{c} {}
}
If I call
B b,c;
A a(b,c);
a.f()
It will print twice "In B"correctly. Notice that A does not override f() so Base::f() is called, this loops and calls B::f() twice, once for b and once for c. However if I go one nested class more, for example by having
class C : public Base
{
A a_;
B b_;
public:
C(A a, B b): Base({&a_, &b_}), a_{a}, b_{b} {}
}
And I call C::f() the program segfaults. I suppose this is because there are some temporaries and their destructors delete the pointers held by Base. Is the solution to this to hold shared_ptr? or is there a better design? I cannot hold unique_ptr cause that would make all derived classes not copyable.
public C(A a, B b): Base({&a, &b}), a{a}, b{b} {}
Firstly, you can't have public there.
Your base points to the parameters of the constructor. Those parameters are destroyed when the constructor finishes and the pointers become invalid. Point to the members instead:
C(A a, B b): Base({&this->a, &this->b}), a{a}, b{b} {}
Note however that the implicit copy and move constructors and assignment operators of the class are broken because they will make the copied object point to the members of the copy source, rather than the members of the copy itself. So, you'll need implement those as well.
is there a better design?
Possibly a better approach: Instead of storing the pointers in the base sub object, write a virtual function that derived classes can override, and which returns a range of pointers to children.
I have two derived classes as follows. This is the simplified design version that I have right now.
class A objects are copy-able but they are big. That is why I used reference in the constructor of the derived_1 class.
I used shared_ptr for the class derived_2 in order to make usage of p_a optional. I noticed that I can also use std::optional in the constructor of the class derived_2. By this I can give hint to the user that this argument is indeed optional.
Please take into consideration that this the simplified version and this member p_a is used in all three classes intensively. Also std::shared_ptr<A&> a is not the only argument for the constructor in the real example. I will be thankful if you show me how to use std::optional properly here.
Is it ok to mix std::optional with std::shared_ptr?
class A
{
int m_a;
public:
A(int a) :m_a(a) {};
};
class Base
{
protected:
std::shared_ptr<A> p_a; //Do I need to change type of p_a???
public:
Base() {};
void print()
{
if (p_a)
std::cout << "p_a is allocated\n";
}
void virtual check() = 0;
};
class derived_1 : public Base
{
public:
derived_1(const A& a)
{
p_a = std::make_shared<A>(a);
}
void check() {};
};
class derived_2 : public Base
{
public:
derived_2(std::shared_ptr<A&> a) //can I use std::optional instead??
{
if (a)
p_a = a;
}
void check() {};
};
I know that it is not possible to have an instance of an abstract class as a base member of another class, i.e.,
#include <iostream>
class Base {
public:
Base() {};
virtual ~Base() {};
virtual int yield() = 0;
};
class C1: public Base {
public:
C1(): Base() {};
virtual ~C1() {};
virtual int yield() {return 1;};
};
class D {
public:
D(Base & b): b_(b) {};
virtual ~D() {};
private:
Base b_;
}
int main() {
C1 c;
D d(c);
}
will fail to compile with the error
test.cpp:22:10: error: cannot declare field ‘D::b_’ to be of abstract type ‘Base’
The obvious workaround is to use (shared) pointers instead. This, however, makes main somewhat more complicated to read,
int main() {
auto c = std::make_shared<C1>();
D d(c);
}
which I would really like to avoid.
Is there a way to keep the main file as simple as in the above example and still achieve the desired functionality?
You can't. When you are creating D it allocates (in heap or in stack) memory for B. And C1 class needs size of base class B plus size of extra variables/etc in C1 itself even if there are nothing new.
So, use pointers instead.
The error caused by virtual int yield() = 0;. If you use virtual int yield(), it will works. When you used virtual int yield() = 0;, it said that the function is a pure virtual function, so you must override it. So you should give its inheritance class and use the instance of inheritance class in class C1. In a world, virtual int yield() = 0; only remind you that it is only a interface, you must override it. I hope this can help you.
Since Base is an abstract class (has at least one pure virtual function), it can't be instantiated directly.
When you declare D's class member as "Base b_", you are effectively trying to create an instance. You can instead use a pointer there (or some kind of safe/smart pointer).
#include <iostream>
class Base {
public:
Base() {};
virtual ~Base() {};
virtual int yield() = 0;
};
class C1: public Base {
public:
C1(): Base() {};
virtual ~C1() {};
virtual int yield() {return 1;};
};
class D {
public:
D(Base * b): b_(b) {};
virtual ~D() {};
private:
Base *b_; // Use a pointer or safe ptr or something of that sort.
}
int main() {
C1 c;
D d(&c);
}
No. One of the properties of an abstract class is that it cannot be instantiated. That means an instance of an abstract class cannot be a member of another class.
Even if Base was not abstract, your class D's constructor would be slicing the object passed. If passed an instance of C1, the copying (in the initialiser list of D's constructor) would not magically cause an instance of D to contain an object of type C. It would instead create a copy only of the Base part of that object.
In short, your design is broken, and will not work even if - syntactically - it would be possible to simplify the code in main().