Look at following code:
class A
{
protected:
int aa = 1;
};
class B : public A
{
private:
int bb = 2;
public:
int getbb() { return bb; }
};
class C : public A
{
private:
int cc = 3;
public:
int getcc() { return cc; }
};
int main()
{
std::vector<A> a;
B b;
C c;
a.push_back(b);
a.push_back(c);
a[0].getbb(); //getbb() unaccessible;
a[1].getcc(); //getcc() unaccessible;
}
A is the based class. B and C is the derived classes. I want to set a vector to hold either B or C, and use vector a to hold A. However, since a is a vector containing A's objects, I can't access methods in B and C. Is there anyway to make a[0].getbb() and a[1].getcc() work?
Your vector of A is not capable of holding Bs or Cs, because it stores A by value, resulting in object slicing when B or C is stored. In particular, this means that when you store B, only aa gets stored; bb gets sliced away.
In order to store subclasses without slicing use a container of pointers - preferably, of smart pointers.
This wouldn't help you access functionality specific to B or C without a cast. One way to solve this problem is to give virtual member functions for B's and C's functionality to A, and make calls through A-typed reference of B or C.
Not without invoking undefined behaviour.
The problem is that a.push_back(b) and a.push_back(c) do not append objects b and c to the vector. They create instances of A that hold only the "A parts". This is called object slicing.
So there is no object of type B and no object of type C in the vector.
You force the issue and make your code compile by doing something like
static_cast<B &>(a[0]).getbb();
but this just has undefined behaviour, since it treats a[0] as being of type B when it is really of type A. Which makes it a really bad idea. Although it will (probably) compile, it could do anything - and probably not what you expect.
If your vector contains A * rather than A it is possible. For example;
int main()
{
std::vector<A *> a;
a.push_back(new B);
a.push_back(new C);
B* b = dynamic_cast<B *>(a[0]);
if (b) // if a[0] actually points at a B ....
b->getbb();
else
complain_bitterly();
C *c = dynamic_cast<C *>(a[1]);
if (c)
c->getcc();
else
complain_bitterly();
}
Of course, doing this has practical trap doors as well - such as requiring class A having at least one virtual member. It would be better off to work with a polymorphic base, and override virtual functions.
In other words, your design is broken, so fix it so it doesn't somehow require you to morph an object to a different type.
An alternative to using pointers is to use a vector of std::reference_wrappers and polymorphic classes. Small example below:
#include <functional> // for std::reference_wrapper
#include <iostream>
#include <vector>
class A
{
public:
virtual void printme()
{
std::cout << "A" << std::endl;
}
virtual ~A() = default;
};
class B: public A
{
public:
void printme() override
{
std::cout << "B" << std::endl;
}
};
class C: public A
{
public:
void printme() override
{
std::cout << "C" << std::endl;
}
};
int main()
{
std::vector<std::reference_wrapper<A>> a;
B b;
C c;
a.emplace_back(b);
a.emplace_back(c);
a[0].get().printme(); // need to "get()" the raw reference
a[1].get().printme();
}
Live on Coliru
According the the cpp reference, there seems to be a way to achieve this by using dynamic_cast. You first need to make your vector a vector of pointers to the base class A. Then when accessing any element, you can check if it is a B* (or a C*) by checking the result of the dynamic_cast operator.
From the CPP reference:
dynamic_cast < new_type > ( expression )
... If the cast is successful, dynamic_cast returns a value of type new_type. If the cast fails and new_type is a pointer type, it returns a null pointer of that type...
Accordingly, you can do this:
std::vector<A*> a;
B b;
C c;
a.push_back(&b);
a.push_back(&c);
...
int i = something;
B* pB = dynamic_cast<B*>(a[i]); if(pB != nullptr) pb->getbb();
C* pC = dynamic_cast<C*>(a[i]); if(pC != nullptr) pC->getcc();
p.s: It is highly questionable as design approach though. The recommended OOP approach would be certainly to use a virtual method in the base class A and override it in B and C. But (hopefully) this answers the exact question as stated in the title.
If you're sure they're instances of B and C, use cast:
static_cast<B>(a[0]).getbb();
static_cast<C>(a[1]).getcc();
OK, you may also create a vector of A*:
std::vector<A*> as;
as.push_back(new B);
as.push_back(new C);
B* b = (B*) as[0];
b->getbb();
c->getcc();
Now you only have to remember about freeing objects with delete.
You may use "Type IDs":
class A {
// ...
virtual int getTypeID() { return 0; }
}
class B {
// ...
virtual int getTypeID() { return 1; }
}
// analogically for C
It's virtual but is in prototype of A
Now use:
switch(a.getTypeID()) {
case 0:
// It's normal A
break;
case 1:
// It's B
// ...
break;
case 2:
// It's C
// ...
break;
}
Related
I try to have an abstract base class act like an interface and instantiate a derived class based on user input. I tried to implement it like this
class A
{
public:
virtual void print() = 0;
};
class B : A
{
public:
void print() override { cout << "foo"; }
};
class C : A
{
public:
void print() override { cout << "bar"; }
};
int main()
{
bool q = getUserInput();
A a = q ? B() : C();
a.print();
}
But that does not work. I’m coming from c# and that would be valid c# so I’m looking for an equivalent way of implement it in c++. Could someone please give me a hint? Thanks!
There are two problems with the code in its current state.
By default in C++, a class that inherits from a class or struct will be private inheritance. E.g. when you say class B : A, it's the same as writing class B : private A -- which in C++ restricts the visibility of this relationship only to B and A.
This is important because it means that you simply cannot upcast to an A from outside the context of these classes.
You are trying to upcast an object rather than a pointer or reference to an object. This fundamentally cannot work with abstract classes and will yield a compile-error even if the code was well formed.
If the base class weren't abstract, then this would succeed -- but would perform object slicing which prevents the virtual dispatch that you would expect (e.g. it won't behave polymorphically, and any data from the derived class is not present in the base class).
To fix this, you need to change the inheritance to explicitly be public, and you should be using either pointers or references for the dynamic dispatch. For example:
class A
{
public:
virtual void print() = 0;
};
class B : public A
// ^~~~~~
{
public:
void print() override { cout << "foo"; }
};
class C : public A
// ^~~~~~
{
public:
void print() override { cout << "bar"; }
};
To model something closer to the likes of C#, you will want to construct a new object. With the change above to public, it should be possible to use std::unique_ptr (for unique ownership) or std::shared_ptr (for shared ownership).
After this, you can simply do:
int main() {
auto a = std::unique_ptr<A>{nullptr};
auto q = getUserInput();
if (q) { // Note: ternary doesn't work here
a = std::make_unique<B>();
} else {
a = std::make_unique<C>();
}
}
However, note that when owning pointers from an abstract base class, you will always want to have a virtual destructor -- otherwise you may incur a memory leak:
class A {
public:
...
virtual ~A() = default;
};
You can also do something similar with references if you don't want to use heap memory -- at which point the semantics will change a little bit.
References in C++ can only refer to an object that already has a lifetime (e.g. has been constructed), and can't refer to a temporary. This means that you'd have to have instances of B and C to choose from, such as:
int main() {
auto b = B{};
auto c = C{};
bool q = getUserInput();
A& a = q ? b : c;
a.print(); // A& references either 'b' or 'c'
}
You need to use pointers :
A* a = q ? static_cast<A*>(new B) : new C;
...
delete a;
A ternary is also a special case here, as it takes the type of the first expression.
If C inherited from B here it would not be a problem.
i've stumbled upon this problem while doing multi-base inheritance project for my college work. Example of my problem, and code itself: I'm setting up 3 classes, A, B and C.
B inherits publicly from A.
C inherits publicly from B.
I want to set a method publicly in B, that does take as argument a pointer to an object of class A. However it should be able to use only class A objects, neither B or C.
Problem is that Visual Studio 2013 doesn't show any error, and simply allows for my method to be used by B class object on a C class object, which is exactly the opposite of what i want to achieve. Why is that happening?
Does that mean that inheriting somewhat makes C object being interpreted as of type A, B and C at the same time? If not, is there a direct way to bind a method to be used only on classes that it inherits from (c methods on both A and B objects)? Feel free to correct me if i'm wrong anywhere, i'm still a newbie at programing. Thank you for your help! `
class A
{
private:
int x;
string z;
public:
void SetZ()
{
cout << "Set Z: ";
cin >> z;
}
string GetZ()
{
return this->z;
}
};
class B
:public A
{
public:
void use_base(A* k)
{
cout << "here and now, i'm using " << k->GetZ() << " however i, " << this->GetZ() << ", might want to!";
}
};
class C
:public B
{
void use_base(A* k)
{
cout << "extra";
}
};
int main()
{
A Bob;
B Mark;
Bob.SetZ();
Mark.SetZ();
C Karl;
Mark.use_base(&Karl); // doesn't show any error
return 0;
}`
If B inherits publicly from A, then B* can be implicitly converted to A* so a function that takes A* can be called with B* arguments. To prevent this, you could make the inheritance protected or private. However, that might create other problems.
To prevent accidentally passing a B*, you can declare another overload that takes B* and delete it. This overload will win for B* and C* arguments and cause a compilation error. You can also generalize this approach using templates, and so prevent passing a pointer to any class derived from A, without naming all such classes.
void use_base(A* k) { /* do something */ }
void use_base(B*) = delete;
However, that doesn't stop someone from explicitly casting a B* or C* to A* and calling the A* overload.
Let's say I have a base class 'A' and two classes 'B' and 'C', which are derived from the base class 'A'
I know I can do this
A *a = new B();
or
A *a = new C();
a->handle() //handle is a virtual function in A. class B and C overloaded this function and added its own implementation.
Then it will call the handle function from either B or C object.
But, I have a restriction that I cannot use a pointer in my program. I have to define A as
A a //not A *a
Then how do I implement this so that it calls the handle function from class B or C?
You can't. You cannot take a value of type A and pretend it's a B. That's just bad, and cannot be done.
Odd question. It can't. Not by any sane means. It's an 'A' afterall.
Assuming you know 'I want to call C's virt' you could do disturbing things like:
#include <stdio.h>
class A { public: virtual int foo() { return 1; } };
class B : public A{ public: virtual int foo() { return 2; } };
class C : public A{ public: virtual int foo() { return 3; } };
int main()
{
A a;
C *c = (C*)&a;
int x = c->C::foo(); // EXAMPLE 1
printf("x == %d\n", x);
x = ((C&)a).C::foo(); // EXAMPLE 2
printf("x == %d\n", x);
return 0;
}
Note that example 2 is just the same thing without anything in the middle. Harder to read, but same result.
The key is using C::foo(); Without C:: you will go through the virtual table and the result will be '1'.
If this is homework, then perhaps professor meant to use references (&) instead of pure pointers (*).
A &a = * new B();
or
A &a = * new C();
a.handle()
Though the references in C++ are pretty much the same thing as regular pointers.
I have a class A, which is parent to classes B and C.
And a class X, which is a parent to Y and Z.
class A {};
class B : public A {};
class C : public A {};
class X
{
void foo(A) { std:: cout << "A"; }
};
class Y : public X
{
void foo(B) {std::cout << "B"; }
};
class Z : public X
{
void foo(c) {std<<cout <<"C"; }
};
int main()
{
B b;
C c;
Y y;
Z z;
y.foo(b);//prints B // b is a B, and Y::foo takes a B, hence print B
y.foo(c);//prints A // mismatch between types, fall back and print A
z.foo(b);//prints A // mismatch between types, fall back and print A
z.foo(c);//prints C // c is a C, and Y::foo takes a C, hence print C
std::vector<A> v;
v.push_back(b);
v.push_back(c);
//In this loop, it always prints A, but *this is what I want to change*
for (size_t i = 0; i < v.size(); ++i)
{
z.foo(v.at(i));
y.foo(v.at(i));
}
}
Is it possible to get the items to print the same result as the hard coded calls?
Meaning that I will treat them as their original type, and not its parent type?
or once I put them int a vector of A they will forever be of type A?
What you are seeing is Object Slicing.
You are storing object of Derived class in an vector which is supposed to store objects of Base class, this leads to Object slicing and the derived class specific members of the object being stored get sliced off, thus the object stored in the vector just acts as object of Base class.
Solution:
You should store pointer to object of Base class in the vector:
vector<X*>
By storing a pointer to Base class there would be no slicing and you can achieve the desired polymorphic behavior as well by making the functions virtual.
The right approach is to use a suitable Smart pointer instead of storing a raw pointer in the vector. That will ensure you do not have to manually manage the memory, RAII will do that for you automatically.
This is called slicing. When you push_back your elements into a std::vector<A> it basically copies the elements into newly constructed instances of A. Therefore the part ofs of the object, which come from the derived class will be lost ("sliced off").
In order to avoid slicing you need to use a container which stores pointers instead of elements, so you should use a std::vector<A*> or if your elements are heap allocated preferably a vector of some sort of smartpointer (std::shared_ptr or std::unique_ptr in C++11, boost::shared_ptr or std::tr1::shared_ptr otherwise).
However your code won't work as written, even if you change that:
X, Y and Z all take their parameter by value, while all elements in your vector will have the type A*, so dereferencing them would yield A, so it will still call the wrong method. This could be solved by changing the signatures to always take A& or A* and using dynamic_cast to try casting that into the type:
class X
{
void foo(A*) { std:: cout << "A"; }
};
class Y : public X
{
void foo(A* p) {
if ( dynamic_cast<B*>(p) ) std::cout << "B"; // requires virtual methods in A
else X::foo(p);
}
};
class Z : public X
{
void foo(A*){
if ( dynamic_cast<C*>(p) ) std::cout << "C"; // requires virtual methods in A
else X::foo(p);
}
};
Of course dynamic_cast is a bit costly, but if that's a problem you might want to rethink your design. Furthermore you need to ensure that A, B, C contain some virtual methods (a virtual destructor would be a good idea here anyways), since otherwise dynamic_cast won't work)
ok say we have the following classes
class A
{
public:
virtual void taco()
{
cout << "Class A" << endl;
}
};
class B: public A
{
public:
virtual void taco()
{
cout << "Class B" << endl;
}
};
class C : public A
{
public:
void taco()
{
cout << "Class C" << endl;
}
};
Now if I do this
A a = A();
B b = B();
C c = C();
a.taco(); //Class A
b.taco(); //Class B
c.taco(); //Class C
deque<A> aa = deque<A>();
aa.push_back(a);
aa.push_back(b);
aa.push_back(c);
for(int i=0;i<aa.size();i++)
aa[i].taco();//All Class A
A r = B();
r.taco(); //Class A
Now you'll notice when I initialize A as B or C, it won't fire the functions from B or C. I was wondering if there was any way around this? I understand the concept that since the object is A it uses A's taco function, but I was just wondering if there was some trick to getting at the other functions. My project is fairly complicated, and I can't know all the classes that will override A(due to plugins overriding a class). Also, I kinda need to have the base virtual function have a body to add default behavior. Thanks.
You must store pointers in the deque, since polymorphism only works with reference & pointer types. When you insert those objects into the deque, copies are made of type A, "slicing" off the parts that made them B or C originally.
Similarly, A r = B() just creates a temporary B and copies the A part of it into an A called r.
BTW by A a = A(); you might as well write A a;. They're not completely equivalent, but they do the same job here, and you likely meant for the simpler version.
A a;
B b;
C c;
a.taco(); //Class A
b.taco(); //Class B
c.taco(); //Class C
// With pointers and containers
deque<A*> aa;
aa.push_back(&a);
aa.push_back(&b);
aa.push_back(&c);
for (int i=0; i<aa.size(); i++)
aa[i]->taco(); // Hurray!
// With refs
B q;
A& r = q;
r.taco(); // Class B!
(Just remember that those objects a, b and c have automatic storage duration. The moment they go out of scope, if the deque still exists then all its elements are invalid pointers. You may want to employ dynamic allocation to further control the lifetime of the A, B and C objects.. but I'll leave that as an exercise to the reader.)