When I am writing a demo string class, in the copy assignment function, I try to clear itself through 'delete this' before copy.But it failed.
Str &operator=(const Str &s) {
if (this != &s) { // self-assignment check
//delete this; //cannot run as I imagine
this->~Str();
_size = s._size;
_str = new char[_size + 1];
memcpy(_str, s._str, _size + 1);
}
return *this;
}
~Str() {
_size = 0;
delete[] _str;
}
the linux told me
double free or corruption (out)
Aborted (core dumped)
delete x; is equivalent to x->~T(); followed by operator delete(x) (which is similar to free(x), but might be incompatible with it).
x->~T(); is a dangerous tool. In this case it must be followed by new(this) T(...); (placement-new) to call a constructor, otherwise the current object is considered to be "dead", and any interaction with it causes undefined behavior.
But even if you do call placement-new, it can throw. Then the object remains dead, and the caller gets UB when it tries to destroy the already dead object again.
Conclusion: x->~T(); is hard to use correctly, use something else.
Either write a function that does the same thing as the destrutor, and call it instead. Unlike the destructor, the object wouldn't be considered dead after calling it, so no placement-new is needed.
Or use the copy&swap idiom. Write the assignment as:
Str &operator=(Str s) noexcept
{
std::swap(_str, s._str);
std::swap(_size, s._size);
return *this;
}
This is an universal way of writing assignments. It works both as copy and move assignment, is exception-safe, etc.
Your delete this already executes this->~Str() but that's only one problem. After delete this you call _size = s._size which is actually this->_size = s._size. But lifetime of this already ended so any call to this can lead to undefined behavior. So not delete this nor this->~Str() belong to your assign operator.
Related
MY Previous question:
How's the Copy & Swap idiom really supposed to work, seriously! My code fails
In the code below, I need the variable auto ptr to remain valid and the assertion to pass.
auto ptr = a.data();
Looks like this:
+--------------+
| a.local_data | --\
+--------------+ \ +-------------+
>--> | "Some data" |
+-----+ / +-------------+
| ptr | -----------/
+-----+
#include <iostream>
#include <cassert>
using namespace std;
class Data
{
private:
char* local_data;
int _size = 0;
inline int length(const char* str)
{
int n = 0;
while(str[++n] != '\0');
return n;
}
public:
Data() {
local_data = new char[_size];
}
Data(const char* cdata) : _size { length(cdata) }{
local_data = new char[_size];
std::copy(cdata, cdata + _size, local_data);
}
int size() const { return _size; }
const char* data() const { return local_data; }
void swap(Data& rhs) noexcept
{
std::swap(_size, rhs._size);
std::swap(local_data, rhs.local_data);
}
Data& operator=(const Data& data)
{
Data tmp(data);
swap(tmp);
return *this;
}
};
int main()
{
Data a("Some data");
auto ptr = a.data(); // Obtains a pointer to the original location
a = Data("New data");
assert(ptr == a.data()); // Fails
return 0;
}
EDIT: To GIVE some perspective, the following runs perfectly well with the Standard C++ String class.
#include <iostream>
#include <string>
#include <cassert>
int main()
{
std::string str("Hello");
auto ptr = str.data();
str = std::string("Bye!");
assert(ptr == str.data());
std::cin.get();
return 0;
}
And, I am trying to achieve the same functionality.
In terms of correctness, contrary to what some comments indicate, your assignment operator looks correct for copy/swap:
Data& operator=(const Data& data)
{
// Locally this code is fine
Data tmp(data);
swap(tmp);
return *this;
}
It makes a copy of the data into tmp, swaps with it. Thus, the current object's new state is a copy of the data, and the object's old state is inside tmp and should be cleaned up in its destructor. This is exception safe.
However, it depends on two key things that you failed to do (as the comments did point out in part):
a non-throwing destructor that cleans up the old state. You omitted this, and it is crucial for proper management of the resources this object owns.
~Data()
{
delete [] local_data;
}
note: you don't need to set it to nullptr, and don't need to check for nullptr, because deleting a null pointer is a noop, and once the destructor begins running, the object ceases to exist (lifetime is over) and so it should never be read again or your program has undefined behavior.
You did not write a copy constructor.
When you don't write a proper copy constructor, the compiler generates one for you that does an element-wise copy. That means you end up with a copy of the pointer, not a copy of the data to which it points! That is an aliasing bug because both objects will point to (and logically "own") the same memory. Whichever is destroyed first will delete the memory and corrupt the memory to which the other still points. Fortunately, a copy constructor is easy to make for your class:
Data(const Data& other) :
local_data{new char[other._size]}
_size{other._size},
{
std::copy(other.local_data, other.local_data + _size, local_data);
}
Things to observe about this copy constructor:
if new[] throws, nothing is leaked. copy() cannot throw. This is exception safe.
the order of initialization is not the order listed in the constructor, but the order the data members are declared in the class. Thus, local_data will be initialized before _size, and so it's important to use other._size for the new expression.
The copy/swap idiom is clean, and concise, and can lead to exception safe code. However, it does have some overhead, as it makes an extra object off to the side, and does the work to swap with it. The benefit of this idiom is when multiple operations can throw exceptions, and you want an "all or nothing" assignment. In your particular class, the only thing that can throw is the allocation of local_data in operator=, and so it is not really necessary to use this idiom in this class.
I think your code should be ok after adding these functions. In this case, you would benefit from a move constructor too, and move assignment too, since copying from an rvalue can be optimized, since we know the temporary is about to be destroyed when the assignment completes, we can "steal" its allocation and not have to create one of our own. This is fast, and also exception safe:
Data(Data&& other) :
local_data{other._local_data}
_size{other._size},
{
// important! This prevents other's destructor from
// deleting the allocation we just pilfered from it.
// Note, other's size and pointer are inconsistent, but it's
// about to be destroyed, so it doesn't matter. If it did,
// then swap both members, but that's needless more work
// in this case.
other._local_data = nullptr;
}
Data& operator=(Data&& other) {
_size = other._size;
swap(local_data, other.local_data);
return *this;
}
[updated to address this]
As for your main() function, the assertion does not look reasonable.
int main()
{
Data a("Some data");
auto ptr = a.data(); // Obtains a pointer to the original location
a = Data("New data");
assert(ptr == a.data()); // ????
return 0;
}
After you assign to a, the pointer should be different, and you should be asserting that the pointers are NOT the same. But in this case, ptr will be pointing to the old address that a held, which has been deleted by the time you get to the assertion. Storing pointers to object internals while modifying those objects is one of the basic recipes for errors.
One last thing: if you write an operator=, or a custom constructor, you almost always need a custom destructor. Always think of these three together as a special relationship. This was called the "Rule of Three": if you write any of them, you almost certainly must write all of them. The rule was expanded to the "Rule of Five" (after c++11) to include move constructors and move assignment. You should read up on these rules, and always think of these special member functions together. Another one to consider (not for this class, but in class design in general) is the best one, the Rule of Zero.
I have two questions which are connected to each other.
I am implemeting a region quad-tree structure. In this QTree class, I have some fields and more importantly a
vector<QTree*> quads;
field that holds four divided regions of current square.
Firstly, I want to implement a destructor for this class. Note that I cannot define anything in public field in the class (If I were, this was a piece of cake!) so I did this:
QTree::~QTree ()
{
if ( quads [ 0 ] != NULL )
{
for ( int i = 0; i < 4; i++ )
{
delete ( quads [ i ] );
}
}
}
According to valgrind, this works ie. no memory leak and error but I am not very sure if it is. Could you state your opinion about destructor's validity?
Second issue is that, in the implementation of overloaded assignment operator of this class if I write after self-assignment check and before anything else in this method this:
delete this;
I take
* Error in ` ': double free or corruption (out): 0x00007fffcb4d46d0 *
: line 3: 4227 Aborted (core dumped) leakcheck
error.
If I write (same code in destructor.)
if ( quads [ 0 ] != NULL )
{
for ( int i = 0; i < 4; i++ )
{
delete ( quads [ i ] );
}
}
instead "delete this;" in overloaded assigment operator, I get no error.
What is wrong, could you clarify me?
EDIT:
I removed copy constructor.
Maybe the problem is using self destruction of an object. Here are some useful information on this: https://isocpp.org/wiki/faq/freestore-mgmt#delete-this
In modern C++, unless you are in very specific cases, you should manage owning pointers using smart pointer classes, like std::unique_ptr or std::shared_ptr.
Note that STL smart pointers can be nicely combined with STL containers, making it possible to implement a kind of "deterministic (reference-count-based) garbage collector".
In your case, I'd consider using:
// Supports copy semantics
std::vector<std::shared_ptr<QTree>> quads;
or:
// This is movable but not copyable
std::vector<std::unique_ptr<QTree>> quads;
depending on your needs and on your design.
In this way, you can enjoy the so called "Rule of Zero": the C++ compiler will automatically implement copy constructor, move constructor, copy assignment, move assignment and destructor for you.
delete in C++ specifically allows 0/nullptr as an argument, so your test is superficial
Safer to write deallocation loop up to actual vector size for ( int i = 0; i != quads.size(); i++ )
What is wrong, could you clarify me?. When you call delete this you not only call to destructor (with quads deallocation), you then dellocate memory of your class, literally taking it out. If class is allocated on the stack, you'll get even more problems. Solution - refactor deallocation and call it from anywhere
private void QTree::deallocate() {
for(int i = 0; i != quads.size(); ++i) {
delete (quads[i]);
}
}
QTree::~Qtree() {
deallocate();
...
}
QTree& QTree::operator=(const QTree& rhs) {
deallocate();
...
}
if I understand correctly, what you are trying to do is:
QTree& operator =(const QTree& rhs) {
// destroy current content
delete this;
// clone and copy content of rhs to current quads
if (rhs.quads[0] != NULL) {
for (int i = 0; i < 4; ++i) {
quads[i] = new QTree(*rhs.quads[i]);
}
}
return *this;
}
So that you can avoid duplicating the code in the destructor
First thing: why delete this is not working as you expect: that is because after calling the object destructor (which is want you want to do), delete this will also free the memory of the object. Your own current object, the one you are going to use to assign copied values from the other object. Calling the destructor without freeing the memory is possible using this->~Qtree()
In fact, it is also possible to call a constructor without allocating memory using the placement new operator, so if you already have a copy constructor, you assignment operator could be:
QTree& operator=(const QTree& rhs) {
this->~QTree();
new(this)(rhs);
return *this;
}
Looks neat, but unfortunately, it is not recommended to do this as it is not exception safe: if the constructor throws an exception, your object is going to be in a bad state.
One of the recommended way to implement the assignment operator is to use a nothrow "swap" method or function:
QTree& operator=(const QTree& rhs) {
QTree tmp(rhs);
swap(tmp)
return *this;
}
Now that I answered the question, there are a few other issues with your code, especially it seems quite unnecessary to use a variable length vector to always store 4 items and always 4 items. Why no simply a QTree[4] array?
I am a student so I apologize up front for not using the correct forum protocols. I have searched for hours on this problem, none of my classmates can help. My assignment is to create a copy constructor, overloaded assignment operator(=) and a destructor (the 'big three') in C++ to manage an array on the heap. What I wrote below in VS13 produces the correct output but I get a debug error: HEAP CORRUPTION DETECTED:c++ crt detected that the application wrote to memory after end of heap buffer
Can anyone give me some guidance on this, I don't even know where to look. Thanks!!
//copy constructor
myList::myList(const myList& source){
cout << "Invoking copy constructor." << endl;
array_capacity = source.array_capacity; //shallow copy
elements = source.elements; //shallow copy
delete[] arrayPointer;
arrayPointer = new double(source.array_capacity); //deep copy
for (int i = 0; i < array_capacity; i++) //copy array contents
{
arrayPointer[i] = source.arrayPointer[i];
}
}
//overloaded assignment operator
myList& myList::operator=(const myList& source){
cout << "Invoking overloaded assignment." << endl;
if (this != &source){
array_capacity = source.array_capacity; //shallow copy
elements = source.elements; //shallow copy
delete[] arrayPointer; //delete original array from heap
arrayPointer = new double(array_capacity); //deep copy
for (int i = 0; i < source.array_capacity; i++) {//copy array contents
arrayPointer[i] = source.arrayPointer[i];
}
}
return *this;
}
//destructor
myList::~myList(){
cout << "Destructor invoked."<< endl;
delete[] arrayPointer; // When done, free memory pointed to by myPointer.
arrayPointer = NULL; // Clear myPointer to prevent using invalid memory reference.
}
There are a couple of problems with your code. First you are invoking delete on arrayPointer but it hasn't been initialized to anything. This could in fact end up deleting memory you have already allocated or result in an excecption or asset in the implementation of delete. Second when you do initialize it you are allocating a single double initialized to the value of source.array_capacity. Notice the parenthesis used in the line below.
arrayPointer = new double(source.array_capacity);
This will certainly result in undefined behavior during the copy as you end up accessing elements outside the bounds of the array. The above line is present in both your copy constructor and copy-assignment operator and should be using square brackets instead like so:
arrayPointer = new double[source.array_capacity];
You also never check to see if there are any elements stored in the source instance of myList. In this case you should likely be assigning nullptr (or NULL in C++03) to arrayPointer.
As a side note you do not really need to assign NULL to arrayPointer in your destructor. Once the object is destroyed it's gone and any attempt to access it after the fact will result in undefined behavior anyway.
Captain Obvlious pointed out the problem in your copy-constructor already.
You will have noticed that the copy-constructor and the assignment-operator contain a lot of the same code but with a subtle difference. In fact this is probably how you ended up with the error (the assignment operator needs to delete[] the old value, but the copy-constructor doesn't.
Code duplication is bad because it leads to subtle errors like this creeping in. A good pattern you can use here is what's called the copy and swap idiom.
The gist is that you define the copy-constructor, and you also define swap. Then you get assignment for free. This works because swap is easier to implement correctly than the assignment operator, and another major benefit is that nothing can go wrong (you don't have to worry about out-of-memory errors and so on).
In your code; keeping your fixed copy-constructor, you could add inside the class definition:
friend void swap( myList &a, myList &b )
{
std::swap( a.array_capacity, b.array_capacity );
std::swap( a.arrayPointer, b.arrayPointer );
std::swap( a.elements, b.elements );
}
and now the assignment operator is very simple:
myList& myList::operator=(const myList &source)
{
myList temp(source);
swap(*this, temp);
return *this;
}
The old resources from the current object are deleted by the destructor of temp. This version doesn't even need to check for self-assignment because std::swap(x, x) is well-defined.
This can even be optimized further by taking source by value instead of by reference, as explained on the linked page.
Below is an exercise from C++ Primer 5th Edition:
Exercise 13.22: Assume that we want HasPtr to behave like a value.
That is, each object should have its own copy of the string to which
the objects point. We’ll show the definitions of the copy-control
members in the next section. However, you already know everything you
need to know to implement these members. Write the HasPtr copy
constructor and copy-assignment operator before reading on.(Page 511)
Codes for Class HasPtr:
class HasPtr
{
public:
//! default constructor
HasPtr(const std::string &s = std::string()):
ps(new std::string(s)), i(0) { }
//! copy constructor
HasPtr(const HasPtr& hp) : ps(new std::string(*hp.ps)), i(hp.i) { }
HasPtr&
operator = (const HasPtr& hp);
~HasPtr()
{
delete ps;
}
private:
std::string *ps;
int i;
};
My code for this copy-assignment operator:
HasPtr&
HasPtr::operator = (const HasPtr &hp)
{
delete ps;
ps = new std::string(*hp.ps);
i = hp.i;
return *this;
}
Codes presented in the following section from this book:
HasPtr&
HasPtr::operator = (const HasPtr &rhs)
{
auto newp = new string(*rhs.ps); // copy the underlying string
delete ps; // free the old memory
ps = newp; // copy data from rhs into this object
i = rhs.i;
return *this; // return this object
}
By executing step by step, I found a slight difference between the two codes. In my code it doesn't change the address to which ps points, whereas the code from the book makes ps point to a new address. I'm wondering whether this subtle difference has any significant meaning? Should I always change the pointer to a new address in similar situations? why?
Your code has a problem with self-assignment and with exceptions: assume that the memory allocation throws a std::bad_alloc exception. When coding, you should always assume that memory allocations can go wrong although the actually rarely do. In the code
delete ps;
ps = new std::string(*hp.ps);
ps would point to stale member when the second line of code throws an exception. Incidentally, if you end up self-assigning the object, you actually delete the memory of the only before accessing it. Thus, it is a good idea to first copy the content of the right hand side, then put things into place, and finally release resource.
As it happens, these are exactly the operations of
the copy constructor
a swap() operation you generally want for any type holding resources
the destructor
The way to leverage these three operation is known as copy and swap idiom:
T& T::operator=(T other) {
this->swap(other);
return *this;
}
Functionally I can only see one difference.
delete ps;
ps = new std::string(*hp.ps);
If memory is low, the call to new std::string might possibly throw an exception. In your case, ps still has the address of the old deleted string -- so it is malformed. If you recover from the exception somebody might dereference ps and bad things will happen.
auto newp = new string(*rhs.ps); // copy the underlying string
delete ps; // free the old memory
ps = newp; // copy data from rhs into this object
In the text book code, ps is not deleted until after the new string is allocated. On exception, ps still points to a valid string so you do not have a malformed object.
How much of a problem that is depends on a few different things, but it is generally better practice to avoid any chance of a malformed object.
Your version is unsafe for self assignment.
delete ps;
ps = new std::string(*hp.ps);
Here,if doing a self assignment, you may be deleting the ps in both the source and destination, making its use in the new statement wrong (tho it may deceptively work on most occasions).
You can assign the value of the new directly into your member variable, but you can't just arbitrarily delete ps, before knowing if you need it.
If you tested for self assignment eg.if (this!=&hp) before doing your code, then it would be better, but still not ideal (see exception safety comments elsewhere).
There are actually two issues in your code:
Self-assignment
Exception safety
You normally want your member functions to provide strong exception guarantee, i.e., when assignment fails (in your case, it can be in operator new or the copy-constructor of string), the program state does not change.
I think a modern practice is to provide a swap function and make the assignment call the copy constructor. Something like:
void HasPtr::swap(HasPtr& rhs)
{
std::swap(this.ps, rhs.ps);
std::swap(this.i, rhs.i);
}
HasPtr(const HasPtr& rhs)
{
ps = new string(*rhs.ps);
i = rhs.i;
}
HasPtr& operator=(const HasPtr& rhs)
{
HasPtr temp(rhs);
this.swap(temp);
return *this;
}
Here is an example of a class that is made available for the + operation.
class A
{
public:
int *array;
A()
{
array = new int[10];
}
~A()
{
delete[] array;
}
A operator+ (const A &b)
{
A c;
for(int i=0; i<10; i++)
c.array[i] += array[i] + b.array[i];
return c;
}
};
int main()
{
A a,b,c,d;
/* puts some random numbers into the arrays of b,c and d */
a = b+c+d;
}
Will a run the destructor before copying the result of b+c+d or not? If not, how do I make sure no memory is leaked?
The + operator overload is designed this way such that no operand is modified.
You need to add an equals operator to A. Also, you will likely want to create a copy constructor.
When a becomes the return from b+c+d, the array pointer in a gets over written without delete[] ever being called on it. You need to make an operator= that deletes the array.
An example of an operator= is below:
A& operator=(A const& a)
{
if (&a != this) {
int* tmp = this->array;
this->array = new int[10];
//copy a.array to this->array
delete[] tmp;
}
return *this;
}
There's a lot of subtleties in this if you're new to operator=.
In particular, the check whether or not a is equal to this is necessary because it's perfectly valid to write:
A a;
a = a;
This would cause a pointless copy, and in most cases of operator= will cause bugs.
The other subtlety is less of a requirement than that one and more of a coding style (though a very wide spread standard). When copying something that is dynamically allocated, you always want to allocate and copy before you release. That way, if new throws an exception (or something else fails), the object is still in a stable state, though with it's old data instead of the new expected dated.
Will this cause memory leak?
Yes, it will. You forgot to add copy constructor and assignment operator. See Rule of Three
You could also use std::vector<int> for A::array instead of int*. In this case you wouldn't need to worry about copy constructor/assignment operator (as long as you don't add something else that must be handled in destrcutor).