meguli
meguli

Reputation: 1526

Using base class rather than base pointer to work on derived class

I have a base abstract class like this;

class X {
public:
    virtual ~X(){}
    virtual doSomething() = 0;
};

Then I am implementing this with couple of classes like Y, Z etc. each having their own constructor, destructor and doSomething implementation. In my main function I do;

int main(){
    std::vector<X *> v;
    X *x1 = new Y();
    X *x2 = new Z();
    v.push_back(x1);
    v.push_back(x2);
    for(auto p : v){
        p->doSomething()
    }
}

This calls respective doSomething implementations as expected. But my problem is that I am using pointers to abstract class to manipulate a hierarchy of derived classes through its base class. This forces me to use new to create instances on the heap and I have to delete them manually afterwards. Is there a way to do something like this;

int main(){
    std::vector<X> v;
    Y x1();
    Z x2();
    v.push_back(x1);
    v.push_back(x2);
    for(auto p : v){
        p.doSomething()
    }
}

So that destructors will be called automatically when I get out of main. I know I can't create a member of abstract class but using pointers to achieve all this seems strange. Surely there must be a way of doing this without pointers and new-delete. It would be great if someone showed me the best practice.

Upvotes: 4

Views: 88

Answers (2)

skypjack
skypjack

Reputation: 50540

Despite the good answer posted by Kerrek SB, the OP asked for a solution (let me say) new-free and it's worth adding one more answer for that.


You can use a std::reference_wrapper.
It follows a minimal, working example:

#include<vector>
#include<functional>
#include<iostream>

class X {
public:
    virtual ~X(){}
    virtual void doSomething() = 0;
};

class Y: public X {
    void doSomething() override {
        std::cout << "Y" << std::endl;
    }
};

class Z: public X {
    void doSomething() override {
        std::cout << "Z" << std::endl;
    }
};

int main(){
    std::vector<std::reference_wrapper<X>> v;
    Y x1{};
    Z x2{};
    v.emplace_back(x1);
    v.emplace_back(x2)
    for(X &p : v){
        p.doSomething();
    }
}

Note that in this case you must guarantee that the lifetime of the stored objects overcomes the one of the vector.
You won't have this problem if you plan to use std::unique_ptrs, as suggested in an other answer by Kerrek SB.

Upvotes: 2

Kerrek SB
Kerrek SB

Reputation: 476950

You want to use an appropriate smart pointer:

std::vector<std::unique_ptr<X>> v;

v.push_back(std::make_unique<Y>());
v.push_back(std::make_unique<Z>());

// all done

(There are of course still dynamic allocations, since you cannot manage an unbounded number of unconstrained types without runtime indirection, but you should never have to think about any of the dynamic management manually. The type system can do all the work for you.)

Upvotes: 5

Related Questions