Assignment operator for dynamic array in C++ - c++

I am trying to understand the following 2 versions of implementation of assignment operator, which is used to assign the other dynamic array to the the instance (class DoubleVector) it was called with.
version 1.
DoubleVector& DoubleVector::operator= (DoubleVector other)
{
swap(vector_, other.vector_);
swap(size_, other.size_);
return *this;
}
version 2.
DoubleVector& DoubleVector::operator= (const DoubleVector& other)
{
double* newVector = new double[other.size_]; // (Try to) allocate new memory
for (int i = 0; i < other.size_; i++)
{
newVector[i] = other.vector_[i];
}
delete[] vector_; // After allocation succeeded we can delete the old array
size_ = other.size_;
vector_ = newVector;
return *this;
}
My questions are:
For the version 1, is there any case that may be missed (e.g. other.size_ = 0)?
For the version 2, why we need to delete the old array (delete[] vector_;) after the allocation succeeded? Is it necessary?
Furthermore, for the version 2, can I just directly assign other to the instance that "=" is called with?
e.g.
DoubleVector& DoubleVector::operator= (const DoubleVector& other)
{
for (int i = 0; i < other.size_; i++)
{
vector_[i] = other.vector_[i];
}
size_ = other.size_;
return *this;
}

Notice that the array passed as a parameter to the two versions of copy operator is different.
In first case there is a DoubleVector value parameter passed by value (copy of passed value is created with copy constructor or copy assignment operator, in this case listed below). Since function operates with copy of data copy is replacement with swap due to efficiency reasons. All corner cases (like other.size == 0) will be processed correctly.
In the second case there is a const DoubleVector & value parameter passed by const reference. No copying of data is performed and to guarantee that external data will not be modified the reference is const (generally it's a good practice to use const qualifiers where applicable). In this case we manually allocate memory for future array (since currently allocated array, if any, may differ in size). realloc may also be used for that reason. Further internal pointer to array is set to newly allocated data: vector_ = newVector;. Before that assignment we must return previously allocated memory by calling delete[] vector_;. Otherwise there will be a memory leak. Consider 10^3 calls to this operator with array of 10^6 doubles.
The second method has one issue. There is no check on self-assignment:
DoubleVector& DoubleVector::operator= (const DoubleVector& other)
{
if (this == &other)
return;
...
}
Copying is a core concept of OOP. There are different solutions common in use: copy on write, reference copy, copy-swap idiom (mentioned in comments) and others.
Additionally modern C++ introduces move concept.
Hope it helps.

Zero will be handled properly. There is nothing wrong with having an empty array. The behavior will result in "vector_" being an empty array.
You have to delete the old "vector_" because a new one is being created and assigned to "vector_". If you did not delete it, then it would be a memory leak.
You don't know the usage of "other" outside of this operator, so you should not do that assignment. The calling function could delete "other" out from under this instance (or visa-versa) and then you would have a crash/error to find/fix.

Related

How to best handle copy-swap idiom with uninitialised memory

As an academic exercise I created a custom vector implementation I'd like to support copying of non-pod types.
I would like the container to support storing elements that do not provide a default constructor.
When I reserve memory for the vector, and then push_back an element (which manages it's own resources and has a copy and assignment operator implemented - I'm ignoring move constructors for the moment) I have an issue using the copy-swap idiom for that type.
Because the swap happens on a type that is still uninitialised memory, after the swap, the destructor which is called for the temporary will attempt to free some piece of uninitialised data which of course blows up.
There are a few possible solutions I can see. One is ensure all non-pod types implement a default constructor and call that (placement new) on each element in the collection. I'm not a fan of this idea as it seems both wasteful and cumbersome.
Another is to memset the memory for the space of the type in the container to 0 before doing the swap (that way the temporary will be null and calling the destructor will operate without error). This feels kind of hacky to me though and I'm not sure if there is a better alternative (see the code below for an example of this) You could also memset all the reserved space to 0 after calling reserve for a bunch of elements but again this could be wasteful.
Is there documentation on how this is implemented for std::vector as calling reserve will not call the constructor for allocated elements, whereas resize will (and for types not implementing a default constructor a constructed temporary can be passed as a second parameter to the call)
Below is some code you can run to demonstrate the problem, I've omitted the actual vector code but the principle remains the same.
#include <iostream>
#include <cstring>
// Dumb example type - not something to ever use
class CustomType {
public:
CustomType(const char* info) {
size_t len = strlen(info) + 1;
info_ = new char[len];
for (int i = 0; i < len; ++i) {
info_[i] = info[i];
}
}
CustomType(const CustomType& customType) {
size_t len = strlen(customType.info_) + 1;
info_ = new char[len];
for (int i = 0; i < len; ++i) {
info_[i] = customType.info_[i];
}
}
CustomType& operator=(CustomType customType) {
swap(*this, customType);
return *this;
}
void swap(CustomType& lhs, CustomType& rhs) {
std::swap(lhs.info_, rhs.info_);
}
~CustomType() {
delete[] info_;
}
char* info_;
};
int main() {
CustomType customTypeToCopy("Test");
// Mimics one element in the array - uninitialised memory
char* mem = (char*)malloc(sizeof(CustomType));
// Cast to correct type (would be T for array element)
CustomType* customType = (CustomType*)mem;
// If memory is cleared, delete[] of null has no effect - all good
memset(mem, 0, sizeof(CustomType));
// If the above line is commented out, you get malloc error - pointer
// being freed, was not allocated
// Invokes assignment operator and copy/swap idiom
*customType = customTypeToCopy;
printf("%s\n", customType->info_);
printf("%s\n", customTypeToCopy.info_);
return 0;
}
Any information/advice would be greatly appreciated!
Solved!
Thank you to #Brian and #Nim for helping me understand the use case for when assignment (copy/swap) is valid.
To achieve what I wanted I simply needed to replace the line
*customType = customTypeToCopy;
with
new (customType) CustomType(customTypeToCopy);
Invoking the copy constructor not the assignment operator!
Thanks!
You don't use copy-and-swap for construction.
You use copy-and-swap for assignment in order to solve the following problem: the left side of the assignment is an already-initialized object, so it needs to free the resources it holds before having the right side's state copied or moved into it; but if the copy or move construction fails by throwing an exception, we want to keep the original state.
If you're doing construction rather than assignment---because the target is uninitialized---the problem solved by copy-and-swap doesn't exist. You just invoke the constructor with placement new. If it succeeds, great. If it fails by throwing an exception, the language guarantees that any subobjects already constructed are destroyed, and you just let the exception propagate upward; in the failure case the state of the target will be the same as it was before: uninitialized.

C++ Move assignment operator: Do I want to be using std::swap with POD types?

Since C++11, when using the move assignment operator, should I std::swap all my data, including POD types? I guess it doesn't make a difference for the example below, but I'd like to know what the generally accepted best practice is.
Example code:
class a
{
double* m_d;
unsigned int n;
public:
/// Another question: Should this be a const reference return?
const a& operator=(a&& other)
{
std::swap(m_d, other.m_d); /// correct
std::swap(n, other.n); /// correct ?
/// or
// n = other.n;
// other.n = 0;
}
}
You might like to consider a constructor of the form: - ie: there are always "meaningful" or defined values stores in n or m_d.
a() : m_d(nullptr), n(0)
{
}
I think this should be rewriten this way.
class a
{
public:
a& operator=(a&& other)
{
delete this->m_d; // avoid leaking
this->m_d = other.m_d;
other.m_d = nullptr;
this->n = other.n;
other.n = 0; // n may represents array size
return *this;
}
private:
double* m_d;
unsigned int n;
};
should I std::swap all my data
Not generally. Move semantics are there to make things faster, and swapping data that's stored directly in the objects will normally be slower than copying it, and possibly assigning some value to some of the moved-from data members.
For your specific scenario...
class a
{
double* m_d;
unsigned int n;
...it's not enough to consider just the data members to know what makes sense. For example, if you use your postulated combination of swap for non-POD members and assignment otherwise...
std::swap(m_d, other.m_d);
n = other.n;
other.n = 0;
...in the move constructor or assignment operator, then it might still leave your program state invalid if say the destructor skipped deleting m_d when n was 0, or if it checked n == 0 before overwriting m_d with a pointer to newly allocated memory, old memory may be leaked. You have to decide on the class invariants: the valid relationships of m_d and n, to make sure your move constructor and/or assignment operator leave the state valid for future operations. (Most often, the moved-from object's destructor may be the only thing left to run, but it's valid for a program to reuse the moved-from object - e.g. assigning it a new value and working on it in the next iteration of a loop....)
Separately, if your invariants allow a non-nullptr m_d while n == 0, then swapping m_ds is appealing as it gives the moved-from object ongoing control of any buffer the moved-to object may have had: that may save time allocating a buffer later; counter-balancing that pro, if the buffer's not needed later you've kept it allocated longer than necessary, and if it's not big enough you'll end up deleting and newing a larger buffer, but at least you're being lazy about it which tends to help performance (but profile if you have to care).
No, if efficiency is any concern, don't swap PODs. There is just no benefit compared to normal assignment, it just results in unnecessary copies. Also consider if setting the moved from POD to 0 is even required at all.
I wouldn't even swap the pointer. If this is an owning relationship, use unique_ptr and move from it, otherwise treat it just like a POD (copy it and set it to nullptr afterwards or whatever your program logic requires).
If you don't have to set your PODs to zero and you use smart pointers, you don't even have to implement your move operator at all.
Concerning the second part of your question:
As Mateusz already stated, the assignment operator should always return a normal (non-const) reference.

C++ overloading the = operator

When overloading the = operator, should one make the contents of one object equal to the contents of the other object OR do you make the pointer of the object point to the same object?
Reading back on the question it seems that the contents should be copied and not the pointers. But I just can't figure it out, So I would be grateful if someone would explain what I should do, I know how to do both, I'm just not sure which one to choose.
class IntObject
{
private:
int *pi_One;
public:
IntObject(void);
IntObject::IntObject(int const &i_one);
~IntObject(void);
IntObject & operator=(const IntObject&);
};
IntObject::IntObject()
{
pi_One = new int(0);
}
IntObject::IntObject(int const &i_one)
{
pi_One = new int(i_one);
}
IntObject::~IntObject(void)
{
delete pi_One;
}
IntObject & IntObject::operator=(const IntObject& c) {
//This copies the pointer to the ints
this->pi_One = c.pi_One;
return *this;
}
It depends on what semantics you want to have in your type. If you want value semantics, then copy the contents (deep copy, as is the case in std::vector), if you want reference semantics (shallow copy, as in std::shared_ptr)
You should definitely copy the contents, not the pointers. Think about what you will do when one of the objects which both hold the same pointer is destroyed; you can't delete the pointer because the other object would be affected as well, but you can't not delete it either because you'd cause memory leaks. You'd have to use reference counting and everything would get a whole lot more complicated.
The contents should be copied (in fact, changing the pointer of the object shouldn't actually be possible - I can't imagine how you would do that - and even if it is somehow, you're not supposed to do it). You also have to take care of the differences between deep and shallow copies, especially if your class contains pointers (or containers with pointers in them).
Now that I think about it, I'm not even sure which pointer you could possibly want to reassign. Unless you are already working with a pointer - those already have an '=' operator that shouldn't be overloaded though.
The principle of least astonishment would say to copy the content. When using operator= on any other object, you wouldn't expect it to copy pointers.
If you keep your destructor as it is, then you should change assignment overload. It would be also wise that nobody is attempting assigning IntObject to itself:
IntObject & IntObject::operator=(const IntObject& c) {
if (this != &c)
{
//This copies the pointer to the ints
*this->pi_One = *c.pi_One;
}
return *this;
}
Otherwise, there will be attempt to free freed memory in IntObject's destructor

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));
}

c++ mystic transfer of class array

class Array
{
double *mx; int mn;
public:
Array();
~Array(){delete []mx};
Array& operator-(Array& b); //first right way
Array operator -(Array b); //wrong way, but I don't understand why
};
Array::Array ()
{
mn=10;
mx=new double[mn];
}
//first, works perfectly
Array& Array::operator -(Array& b)
{
int i=0;
for(i=0;i<mn ;i++)
this->mx[i]-=b.mx[i];
return *this;
}
// here is Error
Array Array::operator -(Array b)
{
int i=0;
for(i=0;i<mn ;i++)
this->mx[i]-=b.mx[i];
}
int main() {
Array x,b;
x=x-b;
}
If I use the first overload , all works right.
But if I use the second, all is compiled well, but when program is executed, i receive many errors like this:
"c++ ** glibc detected *** double free or corruption"
I can't figure out why this occurs.
As I understand, when I call Array Array::operator-(Array b), the object must be copied and all must be well, but there is error.
well i've read that i've to object that are allocated at the same place in the memory. but i've tried to do this:
Array Array::operator +(Array b)
{ Array c;
int i=0;
for(i=0;i<mn;i++)
this->mx[i]+=b.mx[i];
cout<<&this->mx<<" "<<&b.mx<<endl;
exit(0);
return c; }
i 've expected to receive same addresses in memory....
answer is 0xbfb45188 0xbfb45178 why are they equal?
furhermore, when i declare here name of class(A object)
compiler must give a new memory in stack for object
where am i wrong? i dont understand....
Array Array::operator -(Array b)
This line will create a copy of your array. As you don't have a copy constructor the compiler will just make a copy of all the fields including the pointer field "mx". Now you have two objects both pointing to the same allocated memory. When each one is destructed the delete [] will be called....
You need to either write a copy constructor or ensure that no copying takes place.
To do that pass by reference
Array Array::operator -(Array& b)
(That should probably be const too... but that's a different issue)
You violated the rule of three.
operator- should take a reference, otherwise you're performing needless copies. However, it doesn't need to. It certainly should return a value, because a - semantically gives you a new object. When you write c = a-b, you don't expect a or b to change.
As noted above, you don't need to take a reference into operator-, and in your second example you take by value. This is OK, except you have a second bug:
Your Array class has an internal buffer that it news on construction, and deletes when it gets destroyed (~Array).
However, it does not have a user-defined copy constructor, and the buffer is not automatically copied for you; only the pointer mx is copied.
So, when you copy the Array, you now have two objects with a pointer mx pointing to the same buffer. When one copy goes out of scope, that buffer is deleted; some time later, the other copy tries to do the same, and deleteing the same buffer twice is an error.
My suggestions:
Write a copy constructor and operator= into your class Array. Very important.
Have operator- take a reference anyway. It makes mores sense.
Hope that helps.