C++ pointers

Pages: 12
JLBorges wrote:
It is a leak as far as this program is concerned; there is nothing contentious about that.


It is not a leak. The memory remains accessible at all times (and could be satisfactorily deleted at all times). Moreover, there is no question of "not needed any more", since the information pointed to by p1 and p2 is printed out in the last two significant lines of the original code.


This, on the other hand, would be extremely silly.

1
2
3
4
5
6
    p1 = new int;
    *p1 = 42;
    p2 = p1;

    delete p1;
    p1 = new int;
Last edited on
It is best practice to chain up new/delete whenever possible, and not share a memory address with raw pointers.

OR....

Consider using smart pointers, they were added to the C++ stdlib to make leaks far less likely. The smart pointer object does all the memory management for you so you don't have to remember to delete what was newed. There are several "flavors" of smart pointers available, the two most common are std::unique_ptr<> and std::shared_ptr<>.
https://en.cppreference.com/w/cpp/memory

For gits and shiggles I (crudely) rewrote the code to use shared pointers instead of raw pointers:
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
#include <iostream>
#include <memory>

int main()
{
   // int* p1, * p2;
   
   // I prefer to create a single variable per line
   std::shared_ptr<int> p1;
   std::shared_ptr<int> p2;

   p1 = std::make_shared<int>(42);

   p2 = p1;

   std::cout << "*p1 == " << *p1 << '\t';
   std::cout << "*p2 == " << *p2 << '\n';

   *p2 = 53;
   // could have been written using std::make_shared
   // p2 = std::make_shared<int>(53);

   std::cout << "*p1 == " << *p1 << '\t';
   std::cout << "*p2 == " << *p2 << '\n';

   p1 = std::make_shared<int>(88);

   std::cout << "*p1 == " << *p1 << '\t';
   std::cout << "*p2 == " << *p2 << '\n';
}
*p1 == 42       *p2 == 42
*p1 == 53       *p2 == 53
*p1 == 88       *p2 == 53

The smart pointers still need to be dereferenced directly using operator* to get the contained data, same as with raw pointers.

But now there is no possibility of memory leaks because the smart pointers take care of memory management automatically from cradle to grave.

Because initially both smart pointers own the same object (line 14) I chose std::shared_ptr<> instead of std::unique_ptr<>.

This code is a trivial use of pointers so using smart pointers is kinda over-kill.
Last edited on
In the last 12 posts I see a lot of agreement between people.

The only small little detail that people seems to have different opinions about is whether a program such as this ...

1
2
3
4
int main()
{
	int* ptr = new int; 
}

... has a memory leak or not.
Last edited on
> It is not a leak. The memory remains accessible at all times (and could be satisfactorily deleted at all times).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

int main()
{
    int *p1, *p2;

    p1 = new int;
    *p1 = 42;
    p2 = p1;

    cout << "*p1 == " << *p1 << endl;
    cout << "*p2 == " << *p2 << endl;
    *p2 = 53;
    cout << "*p1 == " << *p1 << endl;
    cout << "*p2 == " << *p2 << endl;
    p1 = new int;
    *p1 = 88;
    cout << "*p1 == " << *p1 << endl;
    cout << "*p2 == " << *p2 << endl;


   return 0;
}


g++ -std=c++20 -g -Wall -Wextra -pedantic-errors -fsanitize=leak  main.cpp && ./a.out > null

=================================================================
==30191==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 4 byte(s) in 1 object(s) allocated from:
    #0 0x7f74654f2171 in operator new(unsigned long) (/usr/local/lib64/liblsan.so.0+0x11171)
    #1 0x400a99 in main /tmp/1657639247.2459576/main.cpp:17
    #2 0x7f74647e982f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

Direct leak of 4 byte(s) in 1 object(s) allocated from:
    #0 0x7f74654f2171 in operator new(unsigned long) (/usr/local/lib64/liblsan.so.0+0x11171)
    #1 0x4009b3 in main /tmp/1657639247.2459576/main.cpp:8
    #2 0x7f74647e982f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

SUMMARY: LeakSanitizer: 8 byte(s) leaked in 2 allocation(s).

http://coliru.stacked-crooked.com/a/df0ec28b6a7c7b16
@Peter87,

The differing opinions stem from what the OS (and the compiler implementation) is possibly doing when cleaning up after the program finishes execution.

The standard doesn't require garbage collection AFAIK, that is left up to the programmer.
Last edited on
Your "leak checker" simply points out that delete isn't called before the end. Which we all knew anyway. All the allocated memory is accessible and correctly used in lines 19 and 20 at the end of the program.

I could access the heap memory at any point in that program after it is allocated. Non-used memory doesn't accumulate. So, personally, I don't regard it as leaking.

I'm quite happy to construct a tree or linked list without having to dismantle it again just for the sake of it as the last lines of a program.


Peter87 wrote:
The only small little detail that people seems to have different opinions about is whether a program such as this ...
int main()
{
	int* ptr = new int; 
}

... has a memory leak or not.


Yes, that sums up the dilemma. Whilst I probably would, out of force of habit, put a paired delete statement in ... I don't regard the actual program as leaking anything.

Last edited on
Yes, that is showing that new has been called twice without matching delete. Within the context of the program, there is a memory leak of 8 bytes. However in the context of the process in which the program is executed, there is no memory leak as when the program exits the process will terminate and in so doing will free up all still-allocated resources (usually except in special circumstances -eg embedded system).

However, IMO i still think that all allocated resources should be freed within the program without making assumptions upon what the OS will do when the program exits (as per my previous comments in other threads).
Repeat:

It is a leak as far as this program is concerned; there is nothing contentious about that.

Whether the operating environment cleans up when the sloppy program exits depends: on almost all hosted environments, it would; on many freestanding implementations it won't.
What was the point of repeating that?




There is absolutely no point in repeating that to people who would continue to believe that there just can't be any free standing implementation of C++.
Okay so I think I can see both viewpoints clearly now.

The point of view that JLBorges and I have is based purely on the C++ level of reasoning. If memory has been allocated but not freed when the program ends (based on the C++ standard rules; ignoring optimizations and other implementation details) we consider that a memory leak.

But if you look at it from the OS point of view there are never any memory leaks after the program has ended (assuming the OS cleans up all memory when the program ends). There could still be memory leaks while the program is running but if they happen right at the end they would get cleaned up in such a short period of time that you can essentially consider them not being leaks at all.
I suppose one could contend that short of a hardware failure, no memory ever leaks.
Just power off, reboot, and restart the program, and it is still there.
Topic archived. No new replies allowed.
Pages: 12