im doing an exercise and I cant understand if i'm creating garbage with this program. As far as I understand there shouldn't be any garbage left and I should not use delete in this case, but it makes me wonder because in class the teacher said that every new has to have a delete somewhere.
#include <iostream>
using namespace std;
bool check_nr_in_array(int number, int* array_of_int_pointers[], int index);
int find_index(int number, int* array_of_int_pointers, int index);
bool mod_array_of_pointers(int number, bool presence, int* array_of_int_pointers[], int index);
int main()
{
int* A[1000];
int x;
int cursor = 0;
int alloc_var = 0;
bool state = false;
do
{
cout << "Insert a number: ";
cin >> x;
if(x >= 0)
{
state = mod_array_of_pointers(x, check_nr_in_array(x, A, cursor), A, cursor);
if(state)
alloc_var++;
cursor++;
}
}while(x >= 0);
cout << "Allocated" << alloc_var << " variables." << endl;
for(int i = 0; i < cursor; i++)
cout << *(A[i]) << " ";
return 0;
}
bool check_nr_in_array(int number, int* array_of_int_pointers[], int index)
{
for(int i = 0; i < index; i++)
{
if(*array_of_int_pointers[i] == number)
return true;
}
return false;
}
int find_index(int number, int* array_of_int_pointers[], int index)
{
for(int i = 0; i < index; i++)
{
if(*array_of_int_pointers[i] == number)
return i;
}
return 0;
}
bool mod_array_of_pointers(int number, bool presence, int* array_of_int_pointers[], int index)
{
if(!presence)
{
array_of_int_pointers[index] = new int;
*array_of_int_pointers[index] = number;
return true;
}else{
array_of_int_pointers[index] = array_of_int_pointers[find_index(number, array_of_int_pointers, index)];
return false;
}
}
The program works but i dont know how to check if im creating garbage somewhere. the exercise itself is just creating an array of int pointers, and you ask for a number until the number is negative. If the inserted nr is already somewhere in the array u should copy it's address otherwise you need to allocate another var with new.
fixed the code like below, can somebody check and let me know if it's still leaking ?
#include <iostream>
using namespace std;
bool check_nr_in_array(int number, int* array_of_int_pointers[], int index);
int find_index(int number, int* array_of_int_pointers, int index);
bool mod_array_of_pointers(int number, bool presence, int* array_of_int_pointers[], int index, bool checksum[]);
void deallocate(int* array_of_int_pointers[], bool checksum[], int index);
int main()
{
int* A[1000];
int x;
int cursor = 0;
int alloc_var = 0;
bool state = false;
bool checksum[1000];
do
{
cout << "Inserire un numero: ";
cin >> x;
if(x >= 0)
{
state = mod_array_of_pointers(x, check_nr_in_array(x, A, cursor), A, cursor, checksum);
if(state)
alloc_var++;
cursor++;
}
}while(x >= 0);
cout << "Allocate " << alloc_var << " variabili." << endl;
for(int i = 0; i < cursor; i++)
cout << *(A[i]) << " ";
deallocate(A, checksum, cursor);
return 0;
}
bool check_nr_in_array(int number, int* array_of_int_pointers[], int index)
{
for(int i = 0; i < index; i++)
{
if(*array_of_int_pointers[i] == number)
return true;
}
return false;
}
int find_index(int number, int* array_of_int_pointers[], int index)
{
for(int i = 0; i < index; i++)
{
if(*array_of_int_pointers[i] == number)
return i;
}
return 0;
}
bool mod_array_of_pointers(int number, bool presence, int* array_of_int_pointers[], int index, bool checksum[])
{
if(!presence)
{
array_of_int_pointers[index] = new int;
*array_of_int_pointers[index] = number;
checksum[index] = true;
return true;
}else{
array_of_int_pointers[index] = array_of_int_pointers[find_index(number, array_of_int_pointers, index)];
checksum[index] = false;
return false;
}
}
void deallocate(int* array_of_int_pointers[], bool checksum[], int index)
{
for(int i = 0; i < index; i++)
if(checksum[i] == 1)
delete array_of_int_pointers[i];
}
Use a profiler
The C++ language does not have any commands or functions to tell you if a program leaks memory. For that, you have to use tools like Valgrind to profile your memory usage.
Here is the way Wikipedia begins its article about Valgrind:
You can read the rest, if you want to investigate further. Professional programmers use tools like Valgrind. Students, however, are not expected to master third-party tools at a time when they are still learning C++ itself.
Every
newneeds a correspondingdeleteYour teacher is right!
Best practice is to accomplish that by creating an RAII class to manage an object that uses dynamically allocated memory. The constructor calls operator
newto allocate the object, and the destructor callsdeleteto deallocate it.RAII is an acronym that stands for "resource aquisition is initialization," horrible name that was invented by no less than Bjarne Stroustrup himself. RAII focuses on the idea of ownership. The owner of an object is the one who is responsible for "cleaning up" when that object is deallocated. In the case of dynamically allocated memory, that means calling operator
delete. For a class such asstd::fstream, which is an RAII class, that means closing the file.Exception safety is an overriding reason to use RAII classes. They are the only way to guarantee that an RAII object will be properly disposed of when an exception causes the unexpected termination of a program.
At this stage of your journey into C++, I will assume that you have not yet studied classes, so I will not give any source-code examples of an RAII class here.
The most natural solution to this problem would be to use an RAII class named
std::unique_ptr. Aunique_ptris a smart pointer that behaves almost the same as regular pointer, except that it automatically calls operatordeleteon the object that it points to, when it is deleted itself.Ownership is key. A
std::unique_ptrowns the object it points to. So, when the destructor of astd::unique_ptris invoked, because the pointer itself is being deallocated, it says, in effect, "If I'm getting deleted, I'm taking everything I own with me!"Array
A, in the program below, should be an array ofstd::unique_ptrobjects. If it were, then the dynamically allocated array elements would automatically call delete on the integers they point to. Your program would not need to call operatordeleteat all. And if you also used a function calledstd::make_unique, which—trust me—you would, then your program would not need to call operatornew, either.With few exceptions, professionals do not use
using namespace std;There are drawbacks to
using namespace std;. See Why is "using namespace std;" considered bad practice?. Your textbook probably should not be teaching you to use it.Instead, pros just type
std::every time they need to reference a name from the Standard Library. That is why I have usedstd::cout,std::cin, and so on, in this answer. (There are a few exceptions to this, mostly centered around something called argument-dependent lookup, but we won't go into them here.)Unless your instructor gets mad, you should do the same thing. And delete
using namespace std;from your program!Defining the assignment
Although the OP does not explicity describe the assignment, from reading the code, I was able to determine that is was this:
Based on this, a good place to start is with a simple declaration of variables. There are no globals here. All the variables are defined at the beginning of function
main.The only new thing here is the initialization of array
A. The trailing braces cause each of its elements to be intialized tonullptr. The variablessizeandnare both initialized to0.std::size_tis an alias for an unsigned integer type that is used by the compiler for array sizes and subscripts. It is also the type returned by operatorsizeof. In general, you should prefer it over typeint, when you use an array.std::size_tis defined in the header<cstddef>, so that has been included here.Function
get_int– A safe way get input from the keyboard.I like to call a function every time I ask the user for input. That way, I can validate the user's entries, and ask him to reenter them if they are invalid. You do not want to lock the user into a loop from which there is no exit, but you do want to keep looping until the user either gets it right, or else cancels the input.
That is what function
get_intdoes below. The user can cancel by entering a negative number. Otherwise, garbage (i.e., non-numeric) entries will be rejected.for (;;)is an infinite loop. Like I said, we're going to keep doing this, until the user get's it right. The only way to exit is by entering a valid integer forn, which then triggers the return statement.It is important to test every input operation involving a user to see whether it succeeded or failed. That is what
if (std::cin >> n)does. When a valid integer is entered, the expressionstd::cin >> nwill be implicitly converted to abool, with the valuetrue. If input fails, then it will be converted tofalse, and the input stream will be placed into a "failed" state.After a failed entry, it is important to call
std::cin.clear, followed bystd::cin.ignore. That is how you putstd::cinback into a "good" state, and clear the garbage entry from the input stream. The call to functionignorediscards all the keystrokes up to, and including, the newline character (i.e., "Enter key") at the end of the line.I posted a fancier version of
get_intin this StackOverflow answer. It adds optional range checks for the entered values, and also optionally allows the user to cancel, by pressing Enter on a blank line. It is a little closer to what I use in my own work.The main loop
One unexpected benefit of writing function
get_intis the clean way in which the main loop of the program can now be written. There are two things it tests. First, it checks to see if there is any capacity left in arrayA, and, second, it checks to see if the user entered a non-negative number. When both answers are "yes," we keep looping.The first check is just
size < capacity. Keep looping so long as the allocated size is less than the overall capacity.The second is a little trickier, and is worthy of explantion:
(n = get_int()) >= 0.On the left, we have
(n = get_int()). This is an assignment. Variablengets the value returned fromget_int. What happens next, though, is that the assigned value is returned to the surrounding expression as the "answer" to the assignment. You end up with(n) >= 0. This is exactly the condition under which we want to keep looping. The user entered a non-negative number.Delete array elements
The question in the OP has now become trivial. The allocated array elements have subscripts ranging from
0up to (but not including)size. Run a simple loop that invokes operatordeleteon those elements.I have omitted much of the detail. That is intentional. This should give you a good start, but I want to let you have some of the fun, too!