Aslan986
Aslan986

Reputation: 10324

Use a class as a BaseClass

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

Answers (7)

ltjax
ltjax

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

Matthieu M.
Matthieu M.

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:

  • choose a class given a condition
  • do some work on the instance produced

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:

  • it works perfectly fine without C++11 move semantics
  • it does not require having a virtual destructor in Base

Upvotes: 1

leftaroundabout
leftaroundabout

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:

  1. A pointer "&b" (I'll call it β) to a generic Base object is allocated on the stack
  2. A new Derived1 object (I'll call it d) is allocated on the heap
  3. β 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

dirkgently
dirkgently

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

Qaz
Qaz

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

David W
David W

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

Andres
Andres

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

Related Questions