Abstract class, polymorphism and memory managment

Based on my understanding C++ manages memory for me by cleaning it when variable becomes out of scope.

But how does it work when I use polymorphism and abstract classes, it looks like I have to manage it by my self.

Here is an exampl
I have class A and sub-class B
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class A {
public:
    virtual ~A() {
        std::cout << "Destroy A";
    }
    virtual void sayHi()=0;
};

class B : public A {
public:
    virtual ~B() {
        std::cout << "Destroy B";
    }
    void sayHi() override {
        std::cout << "Hi";
    }
};


Now I have a function
1
2
3
4
void someFunction() {
    A *a = new B();
    a->sayHi();
}


The variable a must be a pointer as A is abstract. And because it is a pointer and dynamically allocates memory, the actual instance of B won't be cleared when function is done. And if this statement is correct it means that I always have to delete pointer manually.

Is it correct understanding?



Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void someFunction() {

    B b ; // object with automatic storage duration

    A& a = b ; // reference to A
    a.sayHi();

    A* pa = std::addressof(b) ; // raw (non-owning) pointer to A
    pa->sayHi() ;

    // object with dynamic storage duration
    // the smart pointer automates the management of its life-time
    // http://en.cppreference.com/w/cpp/memory/unique_ptr
    // http://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique
    // pa2: smart (owning) pointer to A
    std::unique_ptr<A> pa2 = std::make_unique<B>() ; // #incude <memory>
    pa2->sayHi() ;
}
Nice, thank you

so basically to summarize I should avoid usage of "new" unless it is not a dynamic array.

So the next question, if I want "someFunction" to return the reference of A, how should I do it so I don't loose the actual object because of the scope of the function?

> I should avoid usage of "new" unless it is not a dynamic array.

As far as possible, I should avoid usage of "new" altogether (use std::vector<>).


> I want "someFunction" to return the reference of A,
> how should I do it so I don't loose the actual object because of the scope of the function?

We can't return a reference to a local object with automatic storage duration.
(the life time of the object ends when the function returns).

A commonly used technique is to return a smart pointer.

For instance:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <string>
#include <limits>
#include <sstream>
#include <iomanip>
#include <memory>

struct A {

     virtual ~A() = default ;
     virtual std::unique_ptr<A> clone() const = 0 ;

     virtual void test() const = 0 ;
     // ...
};

struct B : A {

     B() { std::cout << "B::default_constructor\n" ; }
     B( const B& ) { std::cout << "B::copy_constructor\n" ; }
     virtual ~B() { std::cout << "B::destructor (object @" << this << ")\n" ; }
     // ...

     virtual std::unique_ptr<A> clone() const override {

         // return a smart pointer (transfer ownership)
         return std::make_unique<B>(*this) ;
    }

    virtual void test() const override { std::cout << "B::test (object @" << this << ")\n" ; }
};

int main() {

     B b ;
     A& a = b ;
     a.test() ;

     auto ptr_cpy = a.clone() ; // now, ptr_cpy owns the object

     // use ptr_cpy. eg.
     ptr_cpy->test() ;

     // destructor of ptr_cpy deletes the object
}
Actually use shared_ptr if more than one object wants to store the pointer. See:

http://www.cplusplus.com/reference/memory/shared_ptr/?kw=shared_ptr

The main difference is that shared_ptr can be copied while unique_ptr can't. Though shared_ptr is slower when copied.
> use shared_ptr if more than one object wants to store the pointer.

Use shared_ptr if and only if the ownership of the object is shared.
It is perfectly acceptable to store raw non-owning pointers.

A shared_ptr represents shared ownership and can be very useful, even essential, but shared ownership isn’t my ideal, and it always carries a cost (independently of how you represent the sharing). It is better (simpler) if an object has a definite owner and a definite, predictable life span. - Stroustrup in 'The C++ Programming language'

ie. in general,
1. Strongly favour ordinary scoped objects to objects owned by unique_ptr
2. Strongly favour unique_ptr over shared_ptr. (shared_ptr should be the last resort.)

What is often talked about on the web is the run-time cost of using shared pointers; in most cases, the more important cost of using shared pointers is the (often needless) increase in the complexity of the design.
My app will run on Arduino platform, I don't think I can use "fancy" c++ features.

Let's complicate the example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Storage {
    A *a;
public:
    void store(A &a) {
        this->a = &a;
    }

    A &getA() {
        return *a;
    }
};

void someFunction(Storage &storage) {
    B b;
    A &a = b;
    storage.store(a);
}

int main() {
    Storage storage{};
    someFunction(storage);
    A &a = storage.getA();
    a.sayHi();

    return 0;
}


So I added Storage class to store some value. It can be linked list or other time of collection. I must use pointer in storage class as A is abstract. "someFunction" just creates instance of B and stores it in the storage. When function is done, "a.sayHi" prints nothing, and I got following warning/error: "libc++abi.dylib: Pure virtual function called!". As I understand the actual object was destroyed and Storage has link to a corrupted memory. There are two questions:
1. How do I make sure that the object is still there after "someFunction" is done
2. Who manages the memory in Storage class? When some other code decides to store new "a" to a storage, do I have to call "delete"? What if this object is used in other places?
Last edited on
1. How do I make sure that the object is still there after "someFunction" is done


Create b on the heap. Use "new" if you have to.

2. Who manages the memory in Storage class?


You do.

When some other code decides to store new "a" to a storage, do I have to call "delete"?


Yes.

What if this object is used in other places?


Then you need to redesign your code.

Playing with raw pointers is tricky for these very reasons. These reasons are why unique_ptr and shared_ptr were added to the standard.
But in this case it is a huge pain to use polymorphism in C++. I have to use pointers because of polymorphism.

If I store object in Storage and exit the "someFunction". The pointer in storage still should have instance of original object, as it is storage. But here it will point to some garbage as object is out of scope, and the only way to solve this , that I am aware of, is to pass by value that won't be efficient as I want to save classes and structures.

Now when object is deleted from storage I don't want to delete it from everywhere else as it is just a storage and somebody may have the reference they need.

Probably I just want to have Garbage collector.

What are strategies to manage memory in such usecase?



> What are strategies to manage memory in such usecase?

Repeat:
For unique ownership, use std::unique_ptr (object is destroyed when it becomes ownerless).
For shared ownership, use std::shared_ptr (object is destroyed when its last owner goes away).

"Now when object is deleted from storage I don't want to delete it from everywhere else as it is just a storage and somebody may have the reference they need." - this would imply that the ownership is shared.
Topic archived. No new replies allowed.