When I read about memory allocation using new and its release, a question arose. Here is a sample code:

const int size_m = 40; int main () { struct car { char *name; }; int QCar; cout << "How many car?"; cin >> QCar; car *pCar = new car[QCar]; for ( int i = 0; i < QCar; i++) { cout << "Car name: "; char temp [size_m]; char >> temp; char *ptemp = new char [strlen(temp)+1]; strcpy(ptemp,temp); pCar[i].name = ptemp; } cout << " Your auto"<< endl; for ( int i = 0; i < QCar; i++) { cout << pCar[i].name; } delete []pCar; system("pause"); return 0; } 

Is this release alone sufficient or necessary? Or otherwise. Just allocating memory twice, and deleting one.

  • I initially tried as @ Kirill21 and @ Wanket advised, but the result is incorrect, because later I rely on the remote address. When I look through the last for loop. - Funnyjoco
  • Add a destructor, where remove the dynamically allocated memory under the name - Pavel Parshin
  • Honestly, I didn’t really understand how to implement it - Funnyjoco

5 answers 5

Almost right :) Now I will explain why "almost."

In C ++, every object has the same thing as a constructor and destructor. If you don’t write them yourself, the compiler will generate them (everything is a bit more complicated, but I simplify as much as I can). The destructor is called when the object is destroyed, so here

 delete []pCar; 

he will be called. But you did not write your destructor, and the generated one has no idea what to do with the name and therefore does not do anything. So you need to add your destructor -

 struct car { char *name; ~car() { delete[]name; } }; 

In this case, it will be enough to write your delete []pCar; - and everything will be correct. Nearly. Why almost? because what if you forget to initialize the name ? And it will show incomprehensibly where, and when deleting it is unclear what can happen, anything. Therefore, let's add a constructor

 car(): name(nullptr) {} 

And everything would be fine, but someone can (maybe even you) write somewhere pCar->name = "ПЦ" . And when you try to release will be trouble. Therefore, it is better to do not a structure, but a class:

 struct car { public: car(): name(nullptr) {} ~car() { delete[]name; } private: char *name; }; 

Is it done? Nearly. Because you need to somehow somehow force name to store the string. Again, we do the constructor

 car(const char * str):name(nullptr) { if (str) { name = new char[strlen(str)+1]; strcpy(name,str); } } 

and the member function to access this line:

 const char* str() const { return name; } 

and the assignment operator to change the name :

 car& operator=(const char * str) { delete[]name; if (str) { name = new char[strlen(str)+1]; strcpy(name,str); } else name = nullptr; } 

Now it can be considered ready. Much more can be added, but the main thing is already in place:

 struct car { public: car(const char * str = nullptr):name(nullptr) { if (str) { name = new char[strlen(str)+1]; strcpy(name,str); } } ~car() { delete[]name; } const char str() const { return name; } car& operator=(const char * str) { delete[]name; if (str) { name = new char[strlen(str)+1]; strcpy(name,str); } else name = nullptr; } private: char *name; }; 

And your code turns into

 car *pCar = new car[QCar]; for ( int i = 0; i < QCar; i++) { cout << "Car name: "; char temp [size_m]; char >> temp; pCar[i] = temp; } cout << " Your auto"<< endl; for ( int i = 0; i < QCar; i++) { cout << pCar[i].str(); } delete []pCar; 

Something like this - but only for a start :)

  • Perhaps, it is necessary to replace 0 with nullptr? - Andrew Romanov
  • @AndrewRomanov Judging by the code, the author is just beginning to learn the basics of the language. And I'm not sure that he is studying C ++ 11 right away, so I don’t climb into the wilds. Of course, it's better, but there are a lot of things better :) It's just a learning code ... - Harry
  • @Harry is not quite the basics, the code is written as a quick example to make it easier for people to present a point of view) - Funnyjoco
  • @Funnyjoco Then feel free to change zeros to nullptr :) - Harry
  • 2
    All persuaded, changed! :) - Harry

In the above code, there are two entities for which memory is dynamically allocated: the car array and for each car name . It is necessary to free the memory allocated for both.
In the current version, the memory allocated for the car array is freed. Memory allocated for car names is not released. You can do this, for example, like this:

 for (int i = 0; (i < QCar); ++i) { delete[] pCar[i].name; } delete[] pCar; 

However, to reduce the likelihood of errors and to free up memory when exceptions occur, it would be better to automate this process: use smart pointers or create your own classes with destructors freeing memory (and you can use string for name ).

  • Yes, I like you did, but according to the debugger I couldn’t understand exactly whether I was right or not. - Funnyjoco

Just how to add a destructor to Car:

 struct car { char *name; car()//для чистоты :name(nullptr) {} ~car() { delete name; } }; 

And it would also be nice to have a copy constructor, since you store a pointer to the area of ​​memory that is worth copying when copying an object.

And if you go through the code, then much is wrong (about the pointer to the memory external to the class car, it’s understandable - encapsulation violation).
Regarding temp variable:
This is a pointer to an array of data that is created on the iteration stack of the loop, while further you copy data there, then this pointer equates to the internal variable car. After an iteration of the loop, the memory (the one pointed to by the temp variable) and name points to nowhere - a drop in access or at one time depends on the content.
Also, the ptemp variable is not released ( strcpy does not copy the pointer, but the contents). -- memory leak.

Use std :: string for strings, a wonderful class that can do everything you need (memory management, copying, memory sharing, etc.)

  • one
    delete checks for 0 itself, verification is not needed. - Harry
  • I didn’t know, thanks for the info, I’ll see, I’ll correct :) - Andrew Romanov
 for (int i = 0; i < QCar; i++) { cout << "Car name: "; char temp[size_m]; //char >> temp; char *ptemp = new char[strlen(temp) + 1]; strcpy(ptemp, temp); pCar[i].name = ptemp; delete[]ptemp; } 
  • You create a temporary dynamic array on each iteration. It is needed only during the iteration, which means how it completes. You must remove the unnecessary memory more with the last action in order to allocate it at the next iteration without leakage. - Kirill21
  • And in pCar[i].name after deleting ptemp then what will happen? - Pavel Parshin
  • @Pavel Parshin - yes, you are right, wrote without thinking, your idea with the destructor is correct. - Kirill21

C pCar is all right, but forgot to clear ptemp.

 pCar[i].name = ptemp; delete[] ptemp 
  • And in pCar[i].name after deleting ptemp then what will happen? - Pavel Parshin