How to delete dynamically allocated object if Out of Scope

Assume, Object 1 function 1 calls Object 2 function which return value, value is dynamically allocated var, then Object 1 function 2 use variable returned above, then I want to deallocate that var or destroy Object 2

Kindly advise
What kind of value/var is it? I.e. is the dynamic allocation really necessary?
Use smart pointers. Then ownership/delete is automatic. See:
https://en.cppreference.com/w/cpp/memory

in particular std::unique_Ptr and std::shared_Ptr
1
2
3
4
5
6
7
{ //apply a scope
  thing object2;
  type* foo = object2.getptr();
  use(foo);
  object2.dostuff();
  delete foo;  //you still have to delete it, unless object2 deconstructor does?
} //things inside scope died here. object 2 is gone now. foo is gone now. object2 dtor is called! 


that should make it obvious that having object2 delete the pointer is good, if it is possible to do so.
OOP and raw pointers don't mix super well. You can almost always break something is you misuse the objects in some way, unless it has a ton of code around it to handle every possible nutty situation.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
auto func2() {
  std::vector<int> arr
  arr.emplace_back( 42 );
  return arr;
} // arr dies here and deallocates the memory used by the element

auto func3() {
  int* x = new int {7};
  return x;
} // x dies here

void func1() {
  auto var1 = func2();
  auto var2 = func3();
  delete var2; // memory that holds 7 dies here
} // var1 and var2 die here 

The vector is kind of a sugar-coated pointer. Both functions thus allocate memory for an int.
The difference is that when function returns a vector, it makes a deep copy of the value.

The func3 returns a pointer and ownership of the dynamically allocated memory is transferred to the caller.


jonnin wrote:
delete foo; //you still have to delete it, unless object2 deconstructor does?

That latter requires that the object2 is able to deallocate, i.e. remains the owner. Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct Obj {
  int* p {};

  auto func1() {
    if (!p) p = new int {13};
    return p;
  }

  ~Obj() {
    delete p;
  }
};

void func1() {
  Obj object2;
  auto var3 = object2.func1();
} // var3 and object2 die here. object2 deallocates memory of 13 

Use what C++ provides for memory management so you don't have to do it manually. Either a container or a smart pointer.

There are slight performance issues to using a container or smart pointer, though for most uses it would be hard to notice. The gain in not having to manage the object lifetime from creation to destruction could actually be a net saving.

The choice of container or smart pointer, there are several for each, depends on how it will be used.
I'm new to smart pointers myself, so can I ask:
What about .dll or .so libraries?
One common rule of thumb for regular dynamic memory was that if something was allocated in a dynamic library, it should be destroyed in that same library.
That's part of why in SDL you see most create functions have a corresponding destroy function. {SDL_CreateWindow() and SDL_DestroyWindow() for instance}
I don't know if a similar rule exists for smart pointers in this case. Is this still something to be concerned about with smart pointers?
Last edited on
newbieg wrote:
One common rule of thumb for regular dynamic memory was that if something was allocated in a dynamic library, it should be destroyed in that same library. That's part of why in SDL you see most create functions have a corresponding destroy function. {SDL_CreateWindow() and SDL_DestroyWindow() for instance}

Is that a requirement or just a good idea for other reasons?

I mean, with classes you could say something similar, that if the class has a constructor to initialize the object it should also have a destructor to clean it up (if necessary). This is not technically required but it's generally considered "good practice".

SDL is written in C so it doesn't have "smart pointers". It doesn't even have destructors which I think is the most obvious reason why it needs functions such as SDL_DestroyWindow.

For shared C++ libraries I can imagine passing any non-trivial standard library type to be problematic if you want ABI compatibility between different implementations. For example, Microsoft might not implement std::shared_ptr the same way as GCC and Clang does. If you passed std::shared_ptr between a shared library you probably need a different DLL depending on whether you compiled your code with Visual C++, MinGW, etc.

I have no experience writing shared library code.
Last edited on
> if something was allocated in a dynamic library, it should be destroyed in that same library.
> Is this still something to be concerned about with smart pointers?

For a standard smart pointer, We can provide a custom deleter which would be responsible for releasing the resources.

For 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
27
28
29
30
31
#include <memory>
#include <cstdlib>
#include <cstdio>

namespace some_lib
{
    template < typename T > using ptr = std::unique_ptr< T[], decltype(&std::free) > ;

    ptr<double> foo( std::size_t n ) noexcept
    { return ptr<double>{ static_cast<double*>( std::calloc( n, sizeof(double) ) ), &std::free } ; }

    std::shared_ptr<std::FILE> open( const char* file_name ) noexcept
    { return std::shared_ptr< std::FILE >{ std::fopen( file_name, "r" ), &std::fclose } ; }
}

int main()
{
    if( auto arr = some_lib::foo(1'000) )
    {
        // use arr
        arr[23] = 45.67 ; // etc.
    }
    // deleter of arr calls std::free

    if( auto pfile = some_lib::open(__FILE__) )
    {
        int ch = 0 ;
        while( ( ch = std::fgetc( pfile.get() ) ) != EOF ) std::putchar(ch) ;
    }
    // deleter of pfile calls std::fclose
} 
newbieg wrote:
What about .dll or .so libraries?
One common rule of thumb for regular dynamic memory was that if something was allocated in a dynamic library, it should be destroyed in that same library.

Each .dll seems to have its own memory, while .so do share.

At least it did seem so two decades ago when we did port an app to Windows.
The app did use "plugins", explicitly linked libraries for some operations and did unload the lib after use.

Main app had data in a tree. It loads library, library adds to tree, i.e. allocates memory, and then library is unloaded. On IRIX, OSX, and Linux that was fine. On Windows a crash.

Since the plugins were written together with the application, custom allocators were added so that everything within the data tree was allocated "by the main executable" (even by code in DLLs).
The original problem only occurred for static linking (not dynamic). This was solved a while ago by MS (VS2015 ??) when they released the shared library versions of the CRT. This is because each non-shared version of the CRT used for static linking would build its own internal heap and thus with multiple heaps you had to release the memory to the correct heap (ie the .dll or .exe that allocated also had to free). By using the shared version of the CRT they all then used the same heap. So now memory allocated in static linked .dll can say allocate and .exe and release etc. Although if you have such old .dll's that they use the non-shared version of the CRT then the problem will still occur for static linking.


PS. if the .exe and .dll were built using different vendor compilers, then for static linking all bets are off...
Last edited on
In general, the subsystem which performs allocation of a resource should be the one that is responsible for deallocating the resource. This principle allows libraries to use their own (often optimised) custom allocation/deallocation/caching strategies; this allows us to look at allocation/deallocation as an internal implementation detail of the module.

For a widely used example, see: getaddrinfo(), freeaddrinfo()
https://man.freebsd.org/cgi/man.cgi?query=getaddrinfo&apropos=0&sektion=3&manpath=FreeBSD+13.1-RELEASE+and+Ports&arch=default&format=html

To use standard smart pointers safely, we also need to ensure that all modules are built with the same compiler / standard library, with compatible compiler options.
Topic archived. No new replies allowed.