boothby81
boothby81

Reputation: 265

having to use pointers to abstract class causes difficulties

My first question here...

I want to use an abstract class A from which i derive a class B (and classes B2, B3, ...). I understood that in order to handle them uniformly, I have to use pointers to the base class, so here variables of type A*.

In the example below, I want to fill aa which is of type vector<A*> in function f. Since I can only use references and the variables lose their scope at the end of f, I cannot access the members of the entries of aa in main anymore.

What could be a solution to this problem?

#include <vector>
#include <iostream>


class A {
    public:
        virtual int getVar(int, int) = 0;
};

class B : public A {
    public:
        B(std::vector<std::vector<int> > var) : var_(var) {};
        int getVar(int i, int j) { return var_[i][j]; }
    private:
        std::vector<std::vector<int> > var_;
};

void f(std::vector<A*>& aa) {
    std::vector<std::vector<int> > var(1, std::vector<int>(1));
    var[0][0] = 42;

    B b(var);

    aa.push_back(&b);
}

int main() {
    std::vector<A*> aa;

    f(aa);

    std::cout << aa[0]->getVar(0, 0) << std::endl;

    return 0;
}

Upvotes: 3

Views: 6244

Answers (3)

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361352

B b(var);
aa.push_back(&b);

It is pushing the address of the local variable. That is the cause of the problem, because the local variable gets destroyed on returning from the functon, but aa still contains the pointer to the non-existing object. Such a pointer is called dangling pointer using which invokes undefined behavior - which is one of the most dangerous and irritating aspect of C++.

Use new:

aa.push_back(new B(var));

Now it should work.

Important : Don't forget to make ~A() virtual:

class A {
    public:
        virtual ~A() {} //MUST DO IT
        virtual int getVar(int, int) = 0;
};

It is necessary otherwise you wouldn't be able to delete objects of derived type (in a well-defined way), using the pointers of base type.

Upvotes: 7

Matthieu M.
Matthieu M.

Reputation: 299790

The problem you observe is a matter of scope.

There are two different allocation strategies in C++:

  • automatic storage: an object declared within a block ({ }), its lifetime ends with the block
  • dynamic storage: an object allocated with new (or new[] if it's an array)

If you want to create a variable and access it outside of its own scope, then you need to rely on dynamic storage. However, it's not free, because then you expose yourself to lifetime management issues, since it's now manual.

The recommendation is therefore to use the RAII idiom, to tie the lifetime of a dynamically allocated object to that of an automatically allocated one. In your case, using smart pointers (or containers).

Using C++11:

int main() {
  std::vector< std::unique_ptr<A> > aa;
  foo(aa);

  std::cout << aa.front()->getVar(0,0) << "\n";
}

And updating foo accordingly:

void foo(std::vector< std::unique_ptr<A> >& aa) {
  std::vector<std::vector<int> > var(1, std::vector<int>(1));
  var[0][0] = 42;

  aa.push_back(new B(var));
}

Upvotes: 3

Constantinius
Constantinius

Reputation: 35039

The problem is within your f() function. You are creating a automatically allocated object, that ceases to exist, once you exit the function. You have to allocat it on the heap, using new.

B* b = new B(var);
aa.push_back(b);

Of course you have to free it on your own when you do not use it any more.

int main() {
    ...


    for (std::vector<A*>::iterator it = aa.begin(); it != aa.end(); ++it)
    {
        delete *it;
    }
    return 0;
}

Upvotes: 2

Related Questions