Vectors within classes: handling copy constructor and destructor (C++) - c++

Take a simple class with the "big 3" (constructor, copy constructor, destructor):
#include <vector>
using namespace std; //actually goes in the C file that links to this header file
...
class planets(){ //stores mass and radii data for planets in a solar system.
public:
vector <double> mass;
vector <double> radius;
//constructor
planets( int numObj ){
for(int i=0; i<numObj; i++){
mass.push_back(8.0); //some default values.
radius.push_back(2.0);
}
}
//copy constructor
planets(const planets &p){
vector <double> mass(p.mass); //copy vectors into new class.
vector <double> radius(p.radius);
}
//destructor
~planets(){
delete mass; //ERROR: (...) argument given to ‘delete’, expected pointer
~radius(); //also causes error: no match for call to(...)
}
}
I plan on making a vector of planets, thus the need for the "big 3":
vector <planets> stars;
stars.push_back(planets(5)); //5 hypothetical planets of alpha centauri
stars.push_back(planets(8)); //our solar system. Used to be nine.
///etc.
How do I delete the mass and radius vectors properly, to avoid memory leaks (do I even have to)?

No, you don't need to do anything because you aren't managing any resources. You only write the Big Three when you're managing a resource, but vector is doing that. It's the one with the Big Three properly written, you just use it.
This is why the single responsibility principle is key in resource management: once you have some class that properly manages a resource, you can simply use it without ever worrying about that resource again. Always split resource management from resource use.
The reason you need the Big Three written in a managing class is because the default special members typically do the wrong thing (they copy, assign, destruct values instead of what the values manage/point at.) But once you're resource is wrapped up (like in a std::vector), everything is just fine. The defaults will copy vector, but that copying is correctly written.
By the way, the Big Three is in the context of managing resources (copying and destroying resources), not created them. So it would be copy-constructor, copy-assignment, and destructor, not default constructor.
For your information, here's how you would do it:
class planets
{
public:
// ...
//copy constructor
planets(const planets &p) : // use an initialization list to initialize
mass(p.mass), // copy-construct mass with p.mass
radius(p.radius) // copy-construct radius with p.radius
{
// what you had before just made a local variable, copy-constructed
// it with p.xxx, then got released (nothing happened to your members)
}
//destructor
~planets()
{
// nothing to do, really, since vector destructs everything
// right for you, but you yes, you would delete any resources
// you managed here
}
};
But don't forget the copy-assignment operator. I recommend the copy-and-swap idiom, and leave that as an exercise for you.
(Remember you don't actually need these, though.)

The 'big three' aren't what you say they are. They are: copy constructor, copy assignment operator and destructor.
vector instances are already copiable and assignable, you don't have to do anything special so with two members of type vector<double> you don't need to provide custom implementations of the big three.
Your copy constructor is incorrect, it doesn't copy the source vectors in to the new class, it just constructs function locals from them which are then discarded. This creates local variables called mass and radius which mask the member variables with the same name.
planets(const planets &p){
vector <double> mass(p.mass); //copy vectors into new class.
vector <double> radius(p.radius);
}
More correct (but unnecessary) would be:
planets(const planets &p)
: mass(p.mass) //copy vectors into new class.
, radius(p.radius)
{
}
Similarly your destructor body should be empty. You only delete pointers which have been allocated with new. As you have straight member variables no special action is required.

You don't have to implement any destructor for your class. The vectors will be automatically destroyed. This is related to the Resource Acquisition Is Initialization pattern.

Your class can be simplified too:
class planets()
{ //stores mass and radii data for planets in a solar system.
public:
std::vector<double> mass;
std::vector<double> radius;
//constructor
planets( int numObj )
{
for(int i=0; i<numObj; i++)
{
mass.push_back(8.0); //some default values.
radius.push_back(2.0);
}
}
}
Your class does not contain any resources (ie pointers)
Therefore you do not need to explicitly manage them. Each class is supposed to know how to:
Copy Itself
Be assigned over
Destroy itselft
The compiler generated copy constructor assignment operator and destructor will automatically call these operations on any member variables (mass and radius) so you don't need too. So in your case the std::vector knows how to correctly do all three operations and therefore you do not need to add any extra code.

No - you don't have to. member variable's destructors are automatically invoked after the containing object's destructor.

In your particular situation you don't have to! You're not storing pointers in the vector. You're not storing pointers to vectors in your planets class(that is you've not dynamically allocated the vector<> object so why trying to delete it).

As you didn't newed mass, you don't need to delete it.
Also, the destructors of mass and radius will be called automatically, you don't need to call them explicitely

~planets(){
mass.clear();
radius.clear();
}
the above should be enough as your member vector objects do not have any pointers.

Related

Destroying a vector of vectors

I'm working on a Matrix class that is implemented with a vector of vectors like so:
class Matrix {
private:
int r, c;
std::vector<std::vector<double> > grid;
public:
//things
Currently I am implementing the destructor, and had a thought about it. Before I (smartly) made the switch to the std::vector I was using a double ** grid. That meant a fair bit of new calls since each pointer pointed to an array (which are also pointers I believe...or errors I encountered have suggested anyhow...). Regardless, the destructor looked like:
Matrix::~Matrix() {
for (int i = 0; i < r; i++) {
delete [] grid[i]
delete [] grid
Now I am trying to replicate that (perhaps unnecessarily?) and have the following:
Matrix::~Matrix() {
std::vector<std::vector<double> >::iterator it = this->grid.begin();
for (it; it != this->grid.end(); it++) {
(*it).clear()
this->grid.clear();
std::vector<std::vector<double> >().swap(this->grid);
and
Matrix::~Matrix() {
this->grid.clear();
std::vector<std::vector<double> >().swap(this->grid);
My inclination is to go with the latter, since clear() should completely destroy the internal vectors (not just the contents of the internal vectors) and then the copy and swap idiom-ish effect of the temporary vector to deallocate the memory of the original outer vector. The former makes me think that I clear() the contents of each internal vector, but am left with n-number of empty, still in memory, vectors and would have to call the temporary swap mechanic on each one (and thus potentially [untested] impacting my iterator).
Does calling clear() on the outer vector completely deallocate the memory used for the internal vectors by calling their destructor? I am not sure if their destructor deallocates in a manner that the swap idiom does.
Pay attention, this destructor is trickier than you think. Here we go:
⁣
Okay, actually, it's not. You can thank the rule of zero for this nimble implementation. Since std::vector is a well-behaved class (template) that correctly manages its resources through RAII, the default-generated destructor for your class does the right thing™ without you typing anything.
If, for any reason, you still need an explicit signature for that destructor, you can use the following syntax inside your class:
~Matrix() = default;
Or, if you need the definition to be out-of-line, declare the destructor normally but define it thusly:
Matrix::~Matrix() = default;
You don't need to write explicit destructor. default destructor sequence will take care of destruction part when you go out of scope of this 2D vector definition. Each element of 2D vector is a 1D vector and each element of 1D vector is double , so default destruction sequence will first destroy grid[0] as shown in the below figure, which in turn destroy each double element stored in grid[0], after that it will repeat the same for grid[1] and so on.
_____
_____ grid[0] of doubles |_|_|_|....
|_____ grid[1] of double
grid--> |_____ grid[2] of double
|_____ grid[3] of double
|_____ grid[4] of double

push_back(*obj) is calling destructor and is causing SDL_BlitSurface to crash

I am new to SDL programming in C++ and so I'm working on a simple Invaders game.
In it I have several classes, one of which is of course the Alien class. The destructor of Alien calls for SDL_FreeSurface(SPRITEOFALIEN), in order to delete the object.
I also have a vector in the Game class, and all works well if there is only one Alien, however as soon as I spawn another Alien and I call push_back to add it to the vector, the destructor gets called, and so when it comes to actually drawing the vector of Aliens, SDL_BlitSurface crashes the program.
Sample code:
vector<Alien> aliens;
// More code.... and eventually
if (event.key.keysym.sym == SDLK_a) //Add Alien! Debugging
{
alien = new Alien(150, 0); // Alien* alien
aliens.push_back(*alien);
}
// Some more code... advancing the aliens, etc...
std::vector<Alien>::iterator it2;
for (it2 = aliens.begin(); it2 != aliens.end(); ++it2)
SDL_BlitSurface(it2->getSprite(), 0, screen, it2->getRect()); /// CRASH, FIXME
I looked up other similar questions, but they all suggested having a fixed sized vector, which I don't think is a good solution for my case, given that there should be an arbitrary number of aliens (and missiles too, I have the same problem for them).
I also know that it happens because when push_back is called, the vector is copied to a new bigger sized vector and that is why the destructors are called. Yet for some reason, the drawObjects function catches the OLD vector and crashes...
Is there a way around this?
Alien Class Definition:
#include "alien.h"
Alien::Alien(int x, int y)
{
rect.x = x; // x-pos
rect.y = y; // y-pos
sprite = SDL_LoadBMP("alien.bmp");
}
Alien::~Alien()
{
SDL_FreeSurface(sprite)
printf("Deleted Alien\n");
}
void Alien::move()
{
rect.y += SPEED;
}
Your problem is likely coming from copies of Alien. When a copy is destructed it will destroy the surface resource being shared by all of the copies. If multiple copies are being made when you call push_back() -- and I would bet that at least two copies are -- then on this line alone you double-free the SDL_Surface * and this is likely causing the crash.
I would strongly suggest defining a type specifically to manage surfaces in an RAII fashion, and then using that from Alien as you'll get the proper semantics by default.
For example:
struct SDLSurfaceDeleter
{
void operator()(SDL_Surface * p) const
{
if (p) { SDL_FreeSurface(p); }
}
};
typedef std::unique_ptr<SDL_Surface, SDLSurfaceDeleter> UniqueSDLSurface;
Now in your Alien class you have:
private:
UniqueSDLSurface sprite;
And in your constructor:
sprite = UniqueSDLSurface(SDL_LoadBMP("alien.bmp"));
(Or, better yet, initialize it in the initializer list: sprite(SDL_LoadBMP("alien.bmp")). With the initializer list approach you construct the UniqueSDLSurface object right the first time; with the assignment approach you default-construct it and then move-assign a new object. Both will work, but the initializer list approach is cleaner.)
Finally, remove the Alien destructor. Now, the Alien class should be automatically movable, but not copyable. If you get any compile-time errors about copying Alien objects you need to fix those; they were the source of your problem in the first place, and with this code the compiler simply won't let copying happen anymore, which is a good thing!
In a proper C++ standard library, std::vector will move objects when possible during a reallocation, so now you'll be able to use it properly because it will be moving your Alien objects to their new destination instead of copying them.
As a side note, you could similarly use std::shared_ptr to create a shared surface, where the surface is freed when the last std::shared_ptr is destroyed. This might make more sense here since all Alien objects use the same source bitmap; you could load it once and then share it among all of the instances:
std::shared_ptr<SDL_Surface> make_shared_surface(SDL_Surface * surface)
{
return std::shared_ptr<SDL_Surface>(surface, SDLSurfaceDeleter());
}
Further, here you leak memory, because you don't delete the object you allocated with new:
alien = new Alien(150, 0); // Alien* alien
aliens.push_back(*alien);
But you don't need to do this anyway, since you can do:
aliens.push_back(Alien(150, 0));
Or, better yet:
aliens.emplace_back(150, 0);
The essential issue with a vector of varying size is that when the size increases, the vector may have to reallocate and put all its contents in a new location. This will copy all the existing contents and then destroy the old objects.
Since you don't want your object destroyed (that cleans up the SDL surface), we need to arrange that the vector copies and destroys something else. It could be a raw pointer, but a much better approach is to use a smart pointer such as std::unique_ptr.
Try this:
std::vector<std::unique_ptr<Alien>> aliens;
...
if (event.key.keysym.sym == SDLK_a) //Add Alien! Debugging
{
alien = new Alien(150, 0); // Alien* alien
aliens.emplace_back(alien);
}
Then when you use it, you'll need to dereference it...
for (std::unique_ptr<Alien>& that_alien : aliens)
SDL_BlitSurface(that_alien->getSprite(), 0, screen, that_alien->getRect());
This avoids ever using the broken Alien copy constructor. You should go one step further and disable that to make sure it is never called by accident. In your class definition, add:
class Alien
{
// add one of the two lines below
private: Alien(const Alien&) = delete; // if your compiler has = delete implemented
private: Alien(const Alien&); // this is almost as good
};
First of all the fact that you have issue when your objects are copied points that most probably you broke the "rule of three" -http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)
if your objects cannot be copied you should prohibit copy and/or move (making copy/move ctor private for example) at least then compiler will tell you if there is a problem and you try to copy them.
Now to avoid copying your objects in a vector you need to reserve enough space in vector and call emplace upon creation if you can use C++11. Or you should keep objects by pointers. Your code where you create instance dynamically suggest you or somebody else planned to do so, but for whatever reason put it into vector<Alien> by value. Also you should not keep objects by raw pointers in the vector (unless you experienced enough and know what you are doing) so you should use smart pointer, either standard if you use C++11 or from boost or similar library otherwise:
typedef std::shared_ptr<Alien> AlienPtr;
typedef std::vector<AlientPtr> Aliens;
Aliens aliens;
if (event.key.keysym.sym == SDLK_a) //Add Alien! Debugging
{
aliens.push_back( std::make_shared<Alien>( 150, 0 ) );
}
Most probably the Alien class allocates some pointers using new, frees them upon destruction and does not have a copy constructor. In that case, the default implementation is to copy the pointers, not to allocate new copies of the objects. You should write a copy constructor for Alien that does precisely that. If you imagine that member is the only member of Alien (of class Member), that would look like that:
Alien(const Alien & a) {
member = new Member(*member);
}
Of course, if in turn Member allocates some objects, you should make the right copy constructor there too.

Deep copy of a pointer to an array of pointers to objects

Edit: I originally posed this question out of context so I've reworked it. I've left as much as possible unchanged so most of your responses will still apply.
I'm having trouble understanding how to implement a constructor which accepts a pointer to an array of pointers.
I have the following class which contains a member, bodies, of type Body** (i.e. it is a pointer to an array of pointers to body objects).
class Galaxy
{
private:
int n; // Number of bodies in galaxy.
Body** bodies; // Ptr to arr of ptrs to Body objects.
public:
Galaxy();
Galaxy(int, Body**);
// Some other member functions.
};
Here is the implementation of the constructors:
// Default constructor. Initializes bodies to null pointer.
Galaxy::Galaxy() : bodies(NULL) {}
// Alternate constructor. Here I try to perform a deep copy of bodiesIn.
Galaxy::Galaxy(int nIn, Body** bodiesIn)
{
n = nIn;
// Allocate memory for an array of n pointers to Body objects.
bodies = new Body*[n];
// Perform deep copy.
for (int i=0; i<n; i++)
{
bodies[i] = new Body;
*bodies[i] = *bodiesIn[i];
}
}
Is this method sound, or is there a preferred way to construct such an object.
P.S. I realize it would be easier to code this with std::vector's, however the size of the array doesn't change, and minimizing memory usage is important.
There's lots wrong with your function:
Creating an object and then immediately assigning to it is inefficient, use the copy ctor instead.
If an exception is thrown by any new-expression but the first one or by one of the assignments, you are leaking objects.
Better take a std::size_t for the size, it's designed for it.
Better swap the arguments, that's more idiomatic.
You don't return the copy at the moment
Why not templatize it?
BTW: std::unique_ptr does not add any overhead, but provides plenty of comfort and safety.

When is the constructor called when a vector of class objects is created?

Say I have a class called Foo. Foo doesn't have a default constructor. It has a constructor Foo(int x, float y).
Bar is a container class. It contains a vector that contains instances of Foo.
Bar::Bar(int numberOfFoos, int x, float y) {
foovector.resize (numberOfFoos);
for(int i = 0; i < numberOfFoos; i++) {
**read below**
}
at this point, I want to call the constructor of Foo and pass to it the parameters int x and float y. The constructor of Foo does different things depending on the value of x and y.
Let's say Foo had a default constructor, what exactly does the resize vector function do? Does it simply resize the vector without calling the default constructor? In other words, is space reserved for n elements of type Foo but they aren't initialized to anything??
What if it doesn't have one, like in this case?
in the for loop I want to initialize each Foo element in this way:
foovector[i].Foo(int x, float y);
but I can't call the constructor using the dot access operator in this way. I don't even know at this point if the constructor has already been called by the resize function or not.
Question is, how can I do it?
Another related question about vectors of classes:
In Foo there is a vector that holds floats. the float x parameter is the number of floats that it should hold. Foo's constructor has a line
arrayofFloats.resize (x);
But that means that the computer doesn't know beforehand the size of Foo. And each foo can have different size. Wouldn't it cause problems for a vector of Foo's? How can a vector of specific size be declared if each Foo can have different size?
Sorry for bad english, I hope it has been clear enough.
Thank you.
Don't use resize to reserve space. Instead, use reserve:
foovector.reserve(n); // reserves memory only, no object
// constructions take place
for (std::size_t i = 0; i != n; ++i)
{
foovector.emplace_back(12 * i, i / 3.0); // pushes a new Foo(12*i, i/3.0)
} // at the back of the vector
If I understand this right, you want the Bar constructor to construct numerous instances of Foo within a vector with the same arguments to the Foo constructor each time. IF the Foo constructor works in such a way that the Foo objects will all be identical after construction, you can use std::vector::assign(size_type n, const value_type& val), where value_type in this case is Foo. If you call foovector.assign( numberOfFoos, Foo(x, y) ), the vector will construct one temporary Foo object and fill itself with numberOfFoos copies of that object. assign() also handles all your resizing needs.
But if the Foo constructor involves random behavior, static counters, or something else that causes successive calls to the constructor to result in different objects, copying is not what you want.
Your other question:
In C++, every type has a fixed size. (This is why polymorphism only works with pointers or pass-by-reference semantics.) Many classes, such as std::vector, manage additional memory which can grow or shrink as needed. This is all done behind the scenes with pointers. Additional memory, such as the data contained by a vector, is off in some other memory location and does not affect the actual
size of the object. The vector methods size(), resize(), and reserve() work with that managed memory. So a Foo object is the same size no matter how many items are in its vector.
resize does initialise the new elements; specifically, it value-initialises a temporary object (using the default constructor, if it's a class type), then copy-initialises each element using that.
If it doesn't have a default constructor, or can't be copy-initialised, then you can't use resize.
However, you can use reserve to reserve memory without initialising any objects in it; and then use push_back or insert elements into that space. In C++11, you can also use emplace_back to avoid the need to copy elements:
foovector.reserve (numberOfFoos);
for(int i = 0; i < numberOfFoos; i++) {
foovector.push_back(Foo(42, 1.23)); // C++03, requires copy constructor
foovector.emplace_back(42, 1.23); // C++11, doesn't require copy constructor
}
Regarding your extra question:
But that means that the computer doesn't know beforehand the size of Foo
Yes it does. A vector is a small object containing a pointer to some dynamically allocated memory; its size is fixed, and does not depend on how much memory it allocates.

C++ Memory Error on Delete - Debug Assertion Failed

I am trying to add a set of new ModelImages to a vector, and am getting an error, Debug Assertion Failed, Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse). This occurs when trying to delete the second ModelImage that is generated.
std::vector<ModelImage> ModelImages;
for(int n=0;n<nParamSets;n++)
{
ModelImage* mI = new ModelImage(MOD_WIDTH,MOD_HEIGHT);
ModelImages.push_back(*mI);
delete mI;
}
The constructor and destructor, and copy and swap funcitons, are as follows:
ModelImage(int _width, int _height)
{
width = _width;
height = _height;
nPixels = width*height;
distance = new float[nPixels];
intensity = new float[nPixels];
derivX = new float[nPixels];
derivY = new float[nPixels];
maxDistance = 0.0f;
minDistance = 0.0f;
}
~ModelImage()
{
delete [] derivX;
delete [] derivY;
delete [] distance;
delete [] intensity;
}
ModelImage& operator=(ModelImage other)
{
swap(*this, other);
return *this;
}
friend void swap(ModelImage& first, ModelImage& second)
{
using std::swap;
swap(first.derivX,second.derivX);
swap(first.derivY,second.derivY);
swap(first.distance,second.distance);
swap(first.intensity,second.intensity);
swap(first.nPixels,second.nPixels);
swap(first.width,second.width);
swap(first.height,second.height);
}
Just before trying to delete the second ModelImage, looking at the vector ModelImages shows that the two ModelImages in the vector have the same assigned memory addresses for the distance, intensity, derivX, derivY arrays.
Any help is appreciated, thanks.
This is likely due to you not having a copy constructor.
Create a copy constructor that makes copies of the memory that your pointers are referencing.
When using std containers, they usually will create copies of your object as you insert. As you have no copy constructor, all your member pointers end up pointing to the same memory address because it's simply doing a member-wise copy of your data. Once one of the temporary copies is destructed, (or when you call delete on the original object after the insert) the memory of the object inserted has had it's memory deleted from under it.
My first guess is that you don't have a copy constructor defined. The vectors' push_back will default copy construct your ModelImage which will simply copy the member pointers but not reallocate the memory they point to.
However, these references will be gone after the original objects are deleted.
Hint: A copy constructor is something like:
ModelImage(const ModelImage& orig) {
// appropriately reinitialize from orig
}
Not to confuse with the assignment operator==
Why do you do create these ModelImages dynamically anyway (if you throw them right away)?
And why don't you take vector<float>(nPixels) instead of new float[nPixels]?
It's not clear from what you've posted whether or not you have a proper copy constructor and assignment operator for the following members:
distance
intensity
derivX
derivY
If not, you need those. (see Rule of three (C++ programming) for a bit more information).
A better alternative would be to use std::vector<double> for those data members. That way copying, assignment, and destruction would all be handled automatically. You'd still want to construct them to have the proper number of elements.
I'm assuming that you have all of the arrays defined as pointers in the class. The default copy copies the values of the pointer meaning that when you delete the pointer in the outer function you delete the underlining memory.
Just a couple of suggestions
-Utilize Vector instead of a float * std::vector has copy and move constructors defined allread
-The loop doesn't need to use the free store at all value semantics and coping are fully supported and less error prone.
for(int n=0;n<nParamSets;n++)
{
ModelImages.push_back( ModelImage(MOD_WIDTH,MOD_HEIGHT));
}