Reputation: 10324
I have:
class BASE{
public:
virtual void func1() = 0;
};
Then I have some derived classes, like:
class Derived1 : public BASE{
public:
void func1() {...implementation....}
};
class Derived2 : public BASE{
public:
void func1() {...implementation....}
};
In the main I would like to do something like (pseudo code):
if ( cond ){
BASE b := new Derived1
}
else{
BASE b := new Derived2
}
b.func1();
so that the func1() function invoked is the one specialized for the derived class.
I tried to do:
BASE b = new Derived1();
but the compiler complains.
Is it possible to do that in C++? How?
Upvotes: 1
Views: 149
Reputation: 15997
Use pointers (or references), otherwise your objects will just be copied into an ordinary BASE instance:
BASE* b = new Derived1;
b->func1();
// later...
delete b;
Also, don't forget to make your destructor virtual for this case.
Since the whole new/delete thing is a bit of a hassle just to get polymorphism to work, people tend to use smart-pointers nowadays:
std::shared_ptr<BASE> b=std::make_shared<Derived1>();
b->func1();
Upvotes: 3
Reputation: 299790
The use of pointers (and their traps) have been covered, so I'll show you how to use polymorphism without dynamic memory allocation, probably proving I am an heretic for even thinking about it.
First, let use see your original code, patched up so it compiles:
void foo(bool const cond) {
Base* b = 0;
if (cond) { b = new Derived1(); }
else { b = new Derived2(); }
b->func1();
delete b; // do not forget to release acquired memory
}
which works fine assuming you have a virtual
destructor for Base
.
The next logical step in the evolution of a programmer is to use smart pointers to avoid writing a delete
(delete
are only used by beginners, and expert library writers):
void foo(bool const cond) {
std::unique_ptr<Base> b;
if (cond) { b.reset(new Derived1()); }
else { b.reset(new Derived2()); }
b->func1();
}
it still requires a virtual
destructor for Base
, of course.
Let us realize that this function does two things:
We can break it down, for example by extracting the build work:
std::unique_ptr<Base> build(bool const cond) {
if (cond) { return { new Derived1() }; }
return { new Derived2() };
}
void foo(bool const cond) {
std::unique_ptr<Base> b = build(cond);
b->func1();
}
which is what most people do.
I claim there is another possibility, instead of isolating the build, we can isolate the actual work:
void dowork(Base& b) { b.func1(); /* and perhaps other things */ }
void foo(bool const cond) {
std::unique_ptr<Base> b(cond ? new Derived1() : new Derived2());
work(*b);
}
and we can actually take it one step further:
void foo(bool const cond) {
if (cond) {
Derived1 d;
work(d);
} else {
Derived2 d;
work(d);
}
}
Polymorphism does not require dynamic memory allocation.
And you know what is fun about this last example:
virtual
destructor in Base
Upvotes: 1
Reputation: 120711
Apparently you're used to garbage-collected OO languages such a Java. To answer this question well it's probably good to do it from this point of view.
When you write something
Base b = new Derived1();
in Java, the following happens:
&b
" (I'll call it β
) to a generic Base
object is allocated on the stackDerived1
object (I'll call it d
) is allocated on the heapβ
is set to point at the Derived
object.The reason you get away this easily in Java is that there is a garbage collector. This won't take any effect as long as β
is on the stack and points to d
, because then the GC knows d
is still accessible and possibly in use. But as soon as no pointer refers to d
anymore (e.g. because the function in which you declared b
leaves scope), the GC is permitted to free the space occupied by d
. Easy, but garbage collections has several disadvantages, which we don't want in C++.
So in C++, if you do something like
Base* b = new Derived1();
which directly corresponds to the Java version, you have a problem: when b
leaves the scope, nothing refers to d
anymore, but it still lies on the heap!. You need to delete it yourself
delete b;
(note that this has the great advantage that you can exactly determine at which point it's deleted, while a garbage collector may leave it lying around uselessly for quite a long time and erase it only when you start running out of memory). But once again, this is not quite enough: unlike a garbage collector, your program doesn't automatically know that b
points to a Derived1
object rather than a Base
object, so delete
will delete is thinking it's dealing with a Base
. But it's easy to get across this: include a virtual destructor in the Base
class, like
class Base{
public:
virtual void func1() = 0;
virtual ~Base() {}
};
Now, this need to delete everything by hand is obviously a bit dangerous: if you forget to do it, you have a memory leak, i.e. the object are never removed from the heap as long as your program runs. For this reason, one should use smart pointers instead of plain C-style pointers, this automatically delete the objects they point to upon leaving scope. In C++11, those are defined in the standard library (header <memory>
). You just do
std::unique_ptr<Base> b(new Derived1());
and now, when b
leaves scope, the Derived1
object is automatically deleted.
In all these examples, calling the virtual function works in the same way:
b->func1();
Upvotes: 3
Reputation: 111130
There are two ways to do this:
Use a pointer of type base class:
Base *b = new Derived1;
Use a const reference of type base class if you want to create a derived on copy construction:
Base const& b = Derived;
Use a non-const reference of type base class if you want to assign a derived class object created elsewhere:
Derived d;
Base& b = d;
Nits:
In order to inherit, you need to specify this in the class definition:
class Derived1 : public Base {
Your functions need to have a return value.
class B{
public:
virtual void func() = 0;
};
Upvotes: 2
Reputation: 61910
You're trying to assign a pointer to a normal object.
new Derived1()
This expression returns a pointer. You'll either have to make BASE
a pointer (BASE *
), or just create it on the stack (preferable, Base b;
). In this case though, you'd need pointers (or references) to convert between classes.
Also, you can't create the object inside an if-else statement because it will then go out of scope for when you want to use it later.
Here's something you could do:
BASE *b;
if (cond)
b = new Derived1();
else
b = new Derived2();
b->func1();
Of course you have to remember to delete
your pointer afterwards. Consider simply using a smart pointer instead.
Upvotes: 1
Reputation: 10184
If the function signatures are the same, perhaps an interface declaration would be more appropriate in this situation...then have each class implement the interface, and declare the variable to be of the interface type you declare...
Upvotes: 0
Reputation: 3414
Did you forget this?
class Derived1 : public BASE
{
};
At least I don't see it in your code - the BASE class being derived
Upvotes: 0