C++ rule of five for class that has dynamic memory - c++

So I'm writing the big five for a class that has dynamic int array
struct intSet {
int *data;
int size;
int capacity;
intSet();
~intSet();
intSet(const intSet& is);
intSet(intSet &&is);
intSet &operator=(const intSet& is);
intSet &operator=(intSet &&is);
}
What I got so far:
intSet::intSet(const intSet& is){
this->size=is.size;
this->capacity=is.capacity;
this->data=is.data;
}
intSet::intSet(intSet &&is){
this->size=is.size;
this->capacity=is.capacity;
this->data=is.data;
is.data=nullptr;
}
intSet& intSet::operator=(const intSet& is){
if(&is!=this){
size=is.size;
capacity=is.capacity;
delete [] data;
data=is.data;
data=new int[capacity];
for(int i=0;i<size;i++){
data[i]=is.data[i];
}
}
return *this;
}
intSet& intSet::operator=(intSet &&is){
if(&is!=this){
size=is.size;
capacity=is.size;
delete [] data;
data=is.data;
is.data=nullptr;
}
return *this;
}
intSet::~intSet(){
delete [] this->data;
}
Clearly there's something wrong with it but I am not really familiar with the big five...I searched a lot but still did not find out the answer...

Clearly there's something wrong with it ... did not find out the answer...
The biggest wrong is in the copy constructor.
When you simply copy the pointer, then both the copy and the original point to the same array. When one of them is destroyed, the destructor deletes the pointed array, at which point the pointer in the other object becomes invalid, and its use will have undefined behaviour.
Solution: Allocate a new array instead. In other words: do a deep copy, rather than shallow. If you need help figuring out how to do that, simply take a look at your copy assignment operator implementation (although, you can simplify with std::copy).
The copy assignment operator has flaws as well:
There is a redundant data=is.data;, which is pointless, since data is overwritten on the next line.
Once you have fixed the copy constructor to do a deep copy, like the assignment operator does, both of them will contain duplicated code for allocation of new array and copying contents. Having duplicate code is slightly bad.
The operator does not provide a strong exception guarantee. If allocating the new array throws an exception, the member pointer will be pointing to a deleted array (leading to UB). Even if the allocation succeeds, the copying of contents may result in an exception. If it does, then the partial copy is not rolled back, and the object remains in an inconsistent state. Lack of strong exception guarantee is moderately bad.
A solution to above problems is to use the popular copy-and-swap idiom to implement the copy assignment operator.
Better solution: From what little is shown, your class appears to be re-inventing std::vector. There is hardly ever a need to do that. Simply use std::vector instead.

Related

Does delete[] work properly with generic arrays? If so why does using std::vector::erase on it cause error in freeing memory

I was trying to work with arrays that are circular, and so ended up writing a CircularArray class for which I have attached the code. It uses a generic pointer for an array.
When I try creating a list of such circular arrays using std::vector, I face a problem when I try to use erase on it.
I don't see why this should be the case as I think the destructors and copy constructor work well enough normally.
Can someone please help with this?
Code:
CircularArray Class
template<class T> class CircularArray
{
//Class denoted by 'T' is expected to have a functional assignment operator, i.e. operator=(const T& ext) {} in place
protected:
int size=0;
int ori=0;
T* array;
private:
int pos=0;
public:
CircularArray() : CircularArray(0) {}
CircularArray(int s) {size=s;array=new T[s];}
CircularArray(T* ptr,int s)// : CircularArray(s)
{
size=s;array=new T[s];
for(int i=0;i<size;i++)
array[i]=ptr[i];
}
CircularArray(const CircularArray<T>& arr) : CircularArray(arr.size)
{
for(int i=0;i<size;i++)
array[i]=arr.array[i];
}
~CircularArray() {delete[] array;}
...
Testing Code
int main()
{
std::vector<CircularArray<int>> test;
int *a1=new int[3] {1,2,3},*a2=new int[3] {1,2,3},*a3=new int[3] {1,2,3};
CircularArray<int> n1(a1,3),n2(a2,3),n3(a3,3);
test.push_back(n1);
test.push_back(n2);
test.push_back(n3);
test.erase(test.begin()+1);
for(auto v : test)
{
for(int i=0;i<3;i++)
cout << v[i];
cout << "\n";
}
}
This program gives bad output after encountering the deleted part of the vector. Valgrind says that there is a memory corruption in trying to read freed memory.
What is wrong?
Vector elements must be copy/move assignable, yet you are relying on the default copy assignment operator which does not create any fresh memory. Your assigned objects all share the same memory space, later resulting in a double free.
Your constructors are good but you'll need a copy/move assignment operator too.
Read about the Rule of Five.
Also consider just using a std::vector for backing storage; it'll be much simpler.
Does delete[] work properly with generic arrays?
Yes.
Your (implicitly generated) copy and move assignment operator are wrong. They will copy the member pointer. Then you have two pointers to the same array, and one destructor deletes it once, and another deletes it for a second time, which leads to undefined behaviour.
When manually managing dynamic resource, it is essential to keep track of ownership, and make sure that it is released exactly once. A typical solution is to use a smart pointer. Your class has unique ownership (or it would have, if it didn't accidentally share the ownership in the assignment operators) of the dynamic array, so a unique pointer would be an appropriate choice.
On the other hand, you could use a vector container instead of a smart pointer.

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++ copy assignment syntax comparison - which is better?

I am studying C++ at University, and in the break I am going through Strousrtup's "The CPP Programming Language 4th Edition" to fill in the gaps of my understanding and what we are being taught in class.
In section 3.3.1 he details a code snippet for a simplified version of a vector class (which is type-specific to doubles only):
Vector& Vector::operator=(const Vector& a) {
double* p = new double[a.sz];
for (int i=0; i!=a.sz; ++i)
p[i] = a.elem[i];
delete[] elem;
elem = p;
sz = a.sz;
return *this;
}
Now, I had already written my own version of an overridden copy assignment operator to go along with this simplified quasi-vector before I saw this, which seems to work correctly, but I was wondering, is there anything wrong with deleting the memory allocated that elem points to and then re-initialising it, as I do below, compared to how Stroustrup does it?
vectoR& vectoR::operator=(const vectoR& v) {
delete[] elem;
elem = new double[v.size()];
sz = v.size();
for (int i = 0; i != sz; ++i)
elem[i] = v[i];
return *this;
}
Yes, Strousrtup's way is self-assignment safe. That is, an instance can be assigned to itself
a = a;
Once you've finished that book you may want to scoot through Meyer's "Effective C++" (2005) which is also an excellent text and considers such problems as these.
Stroustrup's implementation will not destroy the existing elem if an exception occurs, and allows self-assignment.
Your version is incorrect, and has undefined behavior. What
happens if new double[v.size()] throws an exception?
In general, you shouldn't do anything which can invalidate an
object until after you've done everything which might throw and
exception. Leaving a point to deleted memory results in an
invalid object, that new can always throw, so you shouldn't
delete elem until after you've done the new.
EDIT:
To be more explicit: from the original poster's suggested
implementation:
delete[] elem;
elem = new double[v.size()];
The first line invalidates the pointer elem, and if there is
an exception in the second line (and new can always throw an
exception), then the assignment operator leaves the object with
the invalid pointer; any further access to this pointer,
including in the destructor of the object, is undefined
behavior.
There are, in fact, many ways of avoiding this problem in this
particular instance:
delete[] elem;
elem = nullptr;
elem = new double[v.size()];
for example (provided that any functions called on the object
can deal with a null pointer), or (what is effectively the same
thing):
delete[] elem;
elem = new (std::nothrow) double[v.size()];
if ( elem == nullptr )
throw std::bad_alloc();
Both of these solutions are in many ways special, however, and
not generally applicable. They also leave the object in
a special state, which may require extra handling. The
usual solution is to do anything that can throw before modifying
any of the state of the object. In this case, the only thing
which can throw is the new, and we end up with Stroustrup's
solution. In more complicated objects, the necessary solution
may be more complicated; one common simple solution is the swap
idiom:
MyType& MyType::operator=( MyType const& other )
{
MyType tmp( other ); // Copy constructor
swap( tmp ); // Member function, guaranteed nothrow
return *this;
}
This works well if you can write a nonthrow swap member
function. You often can, because swapping pointers is a nothrow
(so in this case, all swap would do is swap elem), but it is
not a given. Each case needs to be evaluated individually.
The swap idiom does give the "strong" guarantee: either the
assignment fully succeeds, or the object's state is unchanged.
You don't often need this guarantee, however; it's usually
sufficient that the object be in some coherent state (so that it
can be destructed).
Finally: if your class has several resources, you'll almost
certainly want to encapsulate them in some sort of RAII class
(e.g. smart pointer) or in separate base classes, so that you
can make the constructors exception safe, so that they won't
leak the first resource if allocating the second fails. This
can be a useful technique even in cases where there is only one
resource; in the original example, if elem had been an
std::unique_ptr<double[]>, no delete would have been necessary
in the assignment operator, and just:
elem = new double[v.size()];
// copy...
is all that would be needed. In practice, if real code, cases
where this solves the solution are fairly rare; in real code,
for example, the orginal problem would be solved with
std::vector<double> (and the requirements of std::vector are
such that std::unique_ptr is not really a solution). But they
do exist, and classes like std::unique_ptr (or an even simpler
scoped pointer) are certainly a solution worth having in your
toolkit.

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

How to copy a structure with pointers to data inside (so to copy pointers and data they point to)?

so I have a structure like
struct GetResultStructure
{
int length;
char* ptr;
};
I need a way to make a full copy of it meaning I need a copy to have a structure with new ptr poinnting on to copy of data I had in original structure. Is It any how possible? I mean any structure I have which contains ptrs will have some fields with its lengths I need a function that would copy my structure coping all ptrs and data they point to by given array of lengthes... Any cool boost function for it? Or any way how to create such function?
For the specific scenario you describe, use a std::vector or some other sequence container. If you do so, then simply copying objects of type GetResultStructure will make copies of the pointed-to data as well:
struct GetResultStructure {
std::vector<char> data;
};
GetResultStructure a = GetData();
GetResultStructure b = a; // a and b have distinct, equivalent data vectors
In general, when you do need to implement this yourself, you do so by implementing a copy constructor and a copy assignment operator. The easiest way to do that is to use the copy-and-swap idiom, covered in great detail in What is the copy-and-swap idiom?
It's pretty much up to you to implement that. Normally you want to do it as a copy constructor so you only have to do it in one place. Unfortunately, there's no real magic to avoid telling the computer about how to copy your structure.
Of course, that only applies if your structure really is substantially different from something that's already written. The one you've given looks a lot like a string or (possibly) vector. Unless you really need to implement something new, you're probably better off just using one of those that's already provided.
Both a copy constructor and assignment operator should be implemented (in the way stated above). A technique which may aid in this process, however, is using a dereference operator (*) when copying pointer data. This will copy the pointer data rather than the memory locations. If you do ptr1 = ptr2 it simply sets the memory location of ptr1 to ptr2 which is why we dereference.
For instance, I'll just show a quick example for a copy constructor:
GetResultStructure(const GetResultStructure& other)
: length(other.length), ptr(new char[length]) // <--- VERY _important_ - initialization of pointer
{
// Alternatively, put your initialization here like so:
// ptr = new char[length];
for(int i=0;i<length;++i)
{
ptr[i] = new char;
*ptr[i] = *other.ptr[i]; // Copy the values - not the memory locations
}
}
And then obviously be sure to clean up in your destructor to prevent memory leaks.
Regards,
Dennis M.
GetResultStructure doCopy(GetResultStructure const& copy) {
GetResultStructure newstruct;
newstruct.length = copy.length;
newstruct.ptr = new char[newstruct.length];
memcpy(newstruct.ptr, copy.ptr, newstruct.length*sizeof(char));
return newstruct;
}
Should be simple. Yes, the sizeof(char) isn't really necessary, but there to show what to do for other data types.
Since you tagged it as C++: Write a copy constructor and an assignment operator,
within which you implement your deep copy code:
struct GetResultStructure
{
GetResultStructure(const GetResultStructure& other)
{
// Deep copy code in here
}
GetResultStructure& operator=(const GetResultStructure& other)
{
if (this != &other) {
// Deep copy code in here
}
return *this
}
int length;
char* ptr;
};