I'm really confused, so I have to ask this. I try to write an application, but I don't know how to reach the variables of the derived class, which are in a vector in the Base class.
The code is:
class A {
public:
A() { };
std::vector<A> aVector;
void Foo();
}
class B : public A {
public:
B() { };
int j;
}
void A::Foo() {
aVector.push_back( B() );
// Here I would like to reach B::j, but only the members and variables of A comes in
aVector[0].j; // wrong
B b = aVector[0]; // no suitable user-defined conversion from "A" to "B" exists
// should I use cast? which one?
}
I'm currently learning inheritance and this kind of things through application programming, and now I'm really stuck.
I looked for other questions, but could not find any that solves my problem. If there is, and I missed, then sorry.
You need to store pointers to A so that your new B object won't get "sliced" (see explanation here) when pushed into the vector.
Also, when you want to use specifically a child method / variable on a pointer from the base class, you need to cast it into the proper type
std::vector<A*> aVector;
aVector.push_back(new B());
B* b = (B*)aVector[0];
cout << b->j;
// remember to delete the content of aVector
Casting an object like this can be dangerous if you are not 100% sure that it is of the type you're casting it in.
See this thread for more information on casting (C style, dynamic_cast and static_cast)
Since the vector is declared to hold objects of type A, when you push a B in to the vector, all the B-ness is stripped away from the object that's stored in the vector. This is known as the slicing problem.
When you later try to access the B elements of the objects stored in the vector you can't because they simply don't exist. You don't have a vector of B objects -- you have a vector of A objects.
In order to solve this problem, you need to store A objects not by value, but by reference or by pointer. You can't store references in a vector, so this leaves you with pointers.
This has nothing to with vectors. If B derives from A then the following code:
A a;
B b = a;
is an error (unless there is some method to convert).
This is correct - your vector items you should be able to handle uniformly. If this means the code that uses the vector expects all items to be B then just make a vector<B>. If not, then you have no business converting an A to a B anyway.
You should never try to access derived class members from the base class. The base class should be agnostic about the implementation details of the derived class. What you are doing is not polymorphic. In other words your B instances cannot act like A instances, because you provided no virtual methods and overrode no virtual methods.
The entire design and approach is incorrect. A::Foo() should be a virtual method (Perhaps even abstract). And you should be doing the work in B::Foo().
And another thing, you shouldn't hold a vector of just plain old A. It should be pointers to A. So std::Vector. And that member should be prefixed with the letter m, to indicate it's a member variable of the class. So std::vector mSomething;
Related
I have two classes
class A {
public:
virtual void doStuff() = 0;
};
class B : public A {
int x;
public:
virtual void doStuff() override { x = x*2;} //just example function
};
And another class that modify and use data from the previous
class Foo {
A a;
public:
Foo::Foo(A &a_) : a(a_) {}
};
now I create the objects, and passes to the Foo class
B b;
// edit b attributes,
Foo foo(b);
So at the argument list for the class constructor I know there is not the problem of object slicing, because is a reference, but what is the case at the moment of assign the variable a(a_)?
Since I don't know how much time the object b is going to live I need to make a secure copy. I have a lot of different derived classes from A, even derived from the derived.
Will there be a object slicing?,
Is there a solution to this, or I need to pass pointers (don't want this approach)?
This causes slicing. C++ built in polymorphism only works with pointer/reference semantics.
In fact:
class Foo {
A a;
that won't even compile, because A is not a concrete class.
To fix this, first make virtual ~A(){}; and then pass smart pointers to A around. Either unique or shared.
Failing that you can use your own bespoke polymorphism. The easiers way is to stuff a pImpl smart pointer as a private member of a class and implement copy/move semantics in the holding class. The pImpl can have a virtual interface, and the wrapping class just forwards the non-overridable part of the behaviour to it.
This technique can be extended with the small buffer optimization, or even bounded size instances, in order to avoid heap allocation.
All of this is harder than just using the built in C++ object model directly, but it can have payoff.
To see a famous example of this, examine std::function<Sig> which is a value type that behaves polymorphically.
There will be object slicing with what you currently have. You're calling the A copy-constructor in Foo's constructor, and there aren't virtual constructors.
Having a member variable of type A only reserves enough space within an instance of Foo for an instance of A. There is only dynamic binding with pointers and references (which are pointers under the hood), not with member variables.
You would have to use pointers to get around this or you could rethink whether you really need a set-up like this.
Yes, there is slicing.
There has to be slicing, because a B does not fit inside a A, but it is an A that you are storing inside the class Foo. The B part is "sliced off" to fit; hence the name.
I have two classes set up as below
class A
{}
class B : A
{}
And was trying to have a vector that could hold any object of class A or it's derived types using pointers. I tried to do:
vector<A*> objectsvar;
B var1();
objectsvar[0] = var1;
// Also tried = *var1;
Is there any way to do this sort of thing? to have a container that can hold any of type A or it's derived classes without loosing anything?
Yes, you can do this. Unfortunately, as has already been pointed out in the comments, you made several mistakes trying to implement it:
B var1(); does not call the default constructor but declares a function.
To add an element to a vector, use push_back (orinsert, emplace or emplace_back). In your case, the subscript operater tries to access an element that is not there.
To get the address of a variable, use &. * does the exact opposite, it dereferences a pointer.
What you want is:
vector<A*> objectsvar;
B var1;
objectsvar.push_back(&var1);
Use the uniform initializer {} instead:
B var1{};
Saying B var1(); declares a function in this case.
I have an std::list of base class pointers, all of which point to one of the two derived object classes. An instance of the base class is never declared, and, although the base class is not abstract, every member function is declared as virtual. Consider the code below:
class A
{
public:
A();
...
//member functions
...
protected:
int common_data_1;
std::string common_data_2;
...
};
class B: public A
{
public:
B();
//member functions
...
protected:
std::string class_B_specific_data;
...
};
class C: public A
{
public:
C();
//member functions
...
protected:
std::string class_C_specific_data;
...
};
These classes are instantiated as the appropriate base class via conditional statements and stored in an std::list by the base class pointer simultaneously in the same block of code like so:
std::list<A*> ptrList;
//conditional statements either create a B or C object
//and then that object is appended to the list
if (blahblah = true)
A* entry = new B();
else
A* entry = new C();
ptrList.append(entry);
I need to perform an insertion sort on this container of base class pointers based on an integer value that both derived classes inherit; however, in my previous attempts and upon inspection with a debugger tool, I find that my insertion sort algorithm properly makes the correct comparisons when accessing the integer that the comparison is based on, but I am unable to swap the position of the base class pointers in the std::list. I want to sort this container of pointers so that I can easily print the data in the proper order with a simple for loop.
This is clearly the result of a misunderstanding of pointer semantics, but to much avail I have been unable to find any reference or example that elucidates or solves the issue I am experiencing.
Any result that I have found either on this site or elsewhere solves this problem by using a container of the actual objects instead of a container of pointers to the objects. But, in my case, I can't do this because my code relies on the polymorphic behavior of the base class in order to have one big list of derived objects, instead of multiple lists for each derived object. Obviously, this makes calling member functions of the correct derived class extremely easy, and I would rather not redesign the entire structure of my data if I can avoid it.
If requested, I can post snippets of my code and/or the attempts that I have made to properly swap these pointer positions inside the container; however, I am unsure if this would even be helpful, since I am clearly using the wrong syntax to handle the pointers.
I appreciate any feedback; this problem has been plaguing me for the past few weeks and it is definitely time for me to step back and ask for assistance. I have a feeling that I am over-analyzing this issue, and that is most likely what is preventing me from solving the problem.
Assuming your goal is to sort an existing container, sort has a Compare comp argument that allows your to change its default behavior. To use it, you define a functor (a class that overrides operator()) that knows how you want your pointers to be compared. In this case, you want to define one that compares the common_data_1 that the pointed-to objects have.
class Comparator {
public:
bool operator(A* left, A* right) {
//You can do whatever logic you need here, here's an example:
return (a->common_data_1) < (b->common_data_2);
}
}
Then, call sort on your list:
ptrList.sort(Comparator());
I like #IanPudney's answer, though I typically use a lambda:
ptrList.sort([](A* first, A* second)
{return first->common_data_1 < second->common_data_1;}
);
Replace common_data_1 with whatever data member or function you want to use to sort.
I have got problem with passing inherited class type as argument to method that takes its base class type.
class Base {...}
class Derived : public Base {...}
class Container {
vector<Base*> cont; //1
public:
void addToCont(Base x) { //2
cont.push_back(&x);
}
}
int main() {
Container c;
c.addToCont(Derived(p1,p2)); //3
}
1) I suppose I need to have container of pointers to objects to keep it working
2) Here is error in conversion from Derived to Base
3) I am not supposed to change this call. I tried
Derived d(p1,p2);
c.addToCont(d);
with
addToCont(Base& x)
and it worked for me.
My problem is that I've got 3 derived classes and I don't want to overload the add method 3 times. I guess I will have to add some virtual method or some type-casting to those classes, but I couldn't find anything about that. I am novice in inheritance and quite confused of this. Thanks for all your help.
Some notes:
Must use a vector of pointers to the Base, so that you can handle objects from the hierarchy. Goes without saying that you're probably better off with using some kind of smart pointer instead of raw pointers, but that goes in preferences and how much you love risk.
Using void addToCont(Base x) is wrong because even if you were only adding a Base object, you will be adding a pointer to a local variable (the pass-by-value parameter)
Using void addToCont(Base &x) the way you do it with a local Derived d is wrong too, for the same reasons as before, as soon as d goes out of scope, you're left with a dangling pointer stored in the pointer
Calling addToCont(Derived(...)) passes a temporary object. That must be taken into account when you think about your memory management.
Not sure why you see a need for overloading addToCont for all Derived classes, that's not what you did on void addToCont(Base &x)
The solution (if you keep to the raw pointers) is to do void addToCont(Base *x) there you can pass a pointer to Base or to any Derived. Again, you must be mindful about the memory management. You're Derived object probably needs to be allocated with a new Derived(...) and you must watch about who owns it, and who has responsibility for deleting it (for example, when the Container object is destroyed).
You probably should remember to make virtual the destructor of Base, because you will be destroying Derived objects from Base pointers, and if the destructor is not virtual, the object will only be partially destroyed.
If addToCont(Derived(...)) call is absolutely required, then you might want to consider to use the void addToCont(Base &x) defininition.... but them, you must clone the object before inserting it into the vector:
void addToCont(const Base &x) { //2
cont.push_back(x.clone());
}
But then.. you need a virtual Base *clone() const method to be implemented (at least) in the Derived classes, that will produce a Base pointer with an exact copy of the Derived object, involving extra copies of the objects and extra cloning...
Derived classes are only "possible to use" when they are either references or pointers. If you convert a class to a base-class without a reference or pointer, you won't be able to use it as a derived class later.
If you are actually storing pointers in your container, then I would make it explicit, so:
class Container {
vector<Base*> cont;
public:
void addToCont(Base* x) {
cont.push_back(x);
}
~Container()
{
for(auto a : cont)
{
delete a;
}
}
}
And in main:
Container c;
c.addToCont(new Derived(p1,p2));
Note that in your original code, the Derived(p1, p2) will get destroyed again just after call to addToCont(...), so your array would be pointing to a "dead" element of the Derived class. Which was probably not what you actually wanted (since it's undefined behaviour to ever use that element, and building up a container full of useless elements is pretty pointless)
I've created a class, called vir, with a function move:
class vir
{
public:
vir(int a,int b,char s){x=a;y=b;sym=s;}
void move(){}
};
(It's derived from a class with variables int x, int y, and char sym)
I have derived a class from this, called subvir:
class subvir:public vir
{
public:
subvir(int a,int b,char s){x=a;y=b;sym=s;}
void move();
};
subvir::move()
{
x++;
return;
}
And then I created an array of vir, and put a subvir into it
subvir sv1(0,0,'Q');
vir vir_RA[1]={sv1};
But when I try to use sv1.move():
vir_RA[0].move();
It uses the vir move ({}) rather than the subvir move ({x++}). I have tried making sv1 a vir and vir_RA a vir, and it works, and it also works when I make them both subvir, but I need them to be different. I tried making vir::move() a pure virtual, but then I get an error substantiating the array. Does anyone know how I can get move() to work when I use it from the array?
You are running into a problem called slicing. Use an array of pointers, or something like Boost.ptr_container.
The base class must have virtual functions to get you what you want, making these pure will result in an abstract base class -- something you cannot instantiate. However, you can still create pointers/references to abstract base classes and assign derived class objects to them. Your base class is best represented as:
class vir
{
public:
vir(int a,int b,char s){x=a;y=b;sym=s;}
virtual void move(){}
};
This makes the derived class's move virtual as well. However your move definition lacks a return value and will not compile. Try:
void subvir::move()
{
x++;
return;
}
Note that you need either pointers (as mentioned in the other answers) or references to derived classes for dynamic binding to work. So, instead of an array of vir objects, use an array of base class pointers:
vir* v[ 2 ] = { new subvir(0, 0, 'Q'), new subvir(10, -10, 'P') };
You should also f
Do read up on the following sections of the C++ FAQ Lite:
Inheritance -- Basics
Inheritance -- Virtual Functions
You need an array of pointers in this case, rather than an array of instances. Use vir*[] instead of vir[]
Two things. The array is an array of vir's so of course it uses the vir::move. move() is not a virtual method.
But more important is slicing. You cannot put subclasses into an array. If sizeof vir != sizeof subvir, the array will not line up correctly. Currently they are the same size. But what happens if they aren't.
Yes, basically compiler does not allow subclasses in arrays because
arrays are initialized tightly for the type size, and subtypes tend
to be larger than the parents and it would lead to problems if you could
initialize arrays with subtype values.
What really happens is compiler first allocates array N * size(base_type) bytes.
And then it copies size(base_type) bytes of each of the initialization
objects. if they were of different types, they would get truncated,
and weird things could happen in your code.
Let me consolidate the previous answers.
There are actually two issues here. One is slicing. You are initializing an array of virs with a copy of a subvir. In such cases the compiler slices the vir part out of the subvir and copies it into the array, so you really do get only vir objects there. Now in your particular case, subvir has no additional data members beyond those of vir, so slicing is somewhat degenerate and the vir object looks a lot like a subvir one. However, vir and subvir are different classes and the object in the array ends up being a vir object and not a subvir object disguised as vir. One way the difference between the two would manifest practically, even if both had the same data members, is if vir had virtual functions overloaded by subvir. In that case the vtable pointer in the object in the array would point to vir's vtable, not subvir's. Of course, it would be even more explicit if subvir were to contain additional data members not found in vir.
The second issue is polymorphism. At the point of usage (the call to move()) the compiler thinks you are calling the move() method of an object of type vir (since the array is an array of virs). (The compiler is of course correct in thinking so due to the slicing, degenerate as it may be in this case.) Had it actually been a subvir object as you intented, you could get subvir::move() called by making move() virtual in vir.
To get the desired behavior you could use an array of pointers (but then you would be operating directly on sv1, not a copy of it, unless you first created a copy and initialized the array with a pointer to the copy).