I'm trying to swap an object within itself. It works but when I add a destructor it gives me a double free error. Is there a way to prevent this? The method I'm talking about is void swap(SimpleArray &object).
(Sorry if you read this before I had the wrong info in my post...)
#include "TestType.h"
class SimpleArray {
private:
TestType* pArray;
//TestType* temp;
public:
SimpleArray(TestType *array)
{
this->pArray = array;
}
~SimpleArray() { delete[] pArray; }
SimpleArray() { pArray = 0;}
SimpleArray(const SimpleArray& arg){ pArray = arg.pArray; }
~SimpleArray() { delete[] pArray; }
TestType * get() const{ return pArray; }
bool isNonNull() const { return pArray != 0; }
//TestType* pArray;
void reset(TestType*& p) {this->pArray = p; }
void reset() { pArray = 0; }
void swap(SimpleArray &object) { SimpleArray temp; temp = object; object = *this; *this = temp;}
TestType * release() { pArray = 0; return pArray; }
TestType& getReference(int a) { return *pArray; }
};
This works but once I add the destructor it gives me a "double free or corruption error". How do I solve this? Here's the function in main where it messes up.
bool testGetReleaseSwap() {
SimpleArray array1;
if (array1.get() != 0)
return false;
TestType* directArray1 = new TestType[100];
array1.reset(directArray1);
if (array1.get() != directArray1)
return false;
TestType* directArray2 = new TestType[50];
SimpleArray array2(directArray2);
array1.swap(array2);
if (array1.get() != directArray2 || array2.get() != directArray1)
return false;
array2.swap(array1);
if (array1.get() != directArray1 || array2.get() != directArray2)
return false;
array1.swap(array1);
if (array1.get() != directArray1)
return false;
if (array1.release() != directArray1 || array2.release() != directArray2)
return false;
if (array1.get() != 0 || array2.get() != 0)
return false;
delete[] directArray1;
delete[] directArray2;
return true;
}
The trivial way out here is to invoke temp.release() at the end if your swap method to prevent double deletion.
The underlying issue is much deeper, though. In C++ it is crucial to always maintain strict semantics of who owns something, for example a memory region that requires deletion.
A frequent pattern is that the object that allocates something is also responsible for cleaning up and no one else. This fits nicely with SimpleArray, but the copy constructor breaks it because it multiplies the number of owners!
To implement shared data semantics you have to invest more work (reference counting etc.) or you have to forbid array copying and make the copy constructor private.
A clean way to fix up swap to work without copying the object would be:
void swap(SimpleArray &object) {
TestType* temp = object.pArray;
object.pArray = this->pArray;
this->pArray = temp;
}
(std::swap(object.pArray, pArray); works as well)
Because to swap the memory regions of the array fits nicely with a single-owner pattern, what went wrong here is only the use of the full object copy.
You should read up on resource management and ownership semantics in C++. Your code will always be error prone unless you absolutely know who owns what.
It seems to me that you are trying to implement a class that has shallow copy semantics (and possibly copy-on-write). To do that successfully you need to track how many other owners of the shared data are still around and need to destroy the owned object, when that count reaches zero. You can either use a std::shared_ptr for that or implement the reference counting yourself.
As for the real problem in that specific example, look at what you copy constructor is doing. It is not copying but simply taking another reference (a pointer to be specific) to the object that is already owned by its argument. That by itself already enough to get a double free and your swap testcase is simply exposing that issue.
Related
I am still somewhat new at c++, and I am a little confused about this.
Say we have struct struct_x that uses raw pointers as attributes. Should the raw pointers then be deleted in the copy assignment operator, when they are already allocated? Or should you just assign the new pointee to the pointer?
(I am aware that it is advised to use smart/unique/shared pointers).
Code example:
struct struct_x {
public:
// Some attributes.
char* m_arr nullptr;
size_t* m_len = nullptr;
size_t* m_alloc_len = nullptr;
// Default constructor.
struct_x() {
m_len = new size_t(0);
m_alloc_len = new size_t(0);
}
// Copy constructor.
// Treat "struct_x" as a unique pointer for this example.
struct_x(const struct_x& x) {
// Assign.
m_arr = new char[*x.m_alloc_len + 1];
memcpy(m_arr, x.m_arr,*x.m_len);
m_len = new size_t(*x.m_len);
m_alloc_len = new size_t(*x.m_alloc_len);
}
// Copy assignment operator.
void operator =(const struct_x& x) {
//
//
// OVER HERE
//
// Should the pointers be deleted when they are already allocated?
// Like:
if (m_arr != nullptr) { delete[] m_arr; }
if (m_len != nullptr) { delete m_len; }
if (m_alloc_len != nullptr) { delete m_alloc_len; }
// Or not?
// Assign.
...
}
}
Second question:
Do I need to delete the old m_arr after using memmove?
// Resize.
void resize(const len_t& req_len) {
using T = char; // only for this example.
if (req_len > m_alloc_len) {
if (m_alloc_len == 0) { m_alloc_len = req_len; }
m_alloc_len *= 16;
T* l_arr = new T [m_alloc_len + 1];
memmove(l_arr, m_arr, m_len);
// if (m_arr != nullptr && m_arr) { delete [] m_arr; } // Do i need to delete here, no right?
m_arr = l_arr;
}
}
If you are implementing a string-like class as an exercise, then m_len and m_alloc_len should not be pointers at all. Only m_arr should be a pointer. If you are not doing an exercise, you should be using std::string or perhaps std::vector<char>.
Having said that...
It is perfectly fine and necessary to delete owning raw pointers in assignment operators of resource-managing classes.
There is a caveat though. The assignment operator should be protected against self-assignment. If you do
my_object = my_object;
then without such protection your program will access deleted memory area. You want this:
void operator =(const struct_x& x) {
if (this != &x) {
// contents of your assignment operator
}
}
my_object = my_object is an unlikely assignment to appear in a program, but such things can and do happen, especially if there is indirection involved. Say when doing a[i] = a[j], it is perfectly reasonable to have i == j.
There are other (better) ways to protect against self-assignment. You will encounter them in due course. You probably need to learn about move semantics first.before
I have to write a code that gets a string and turns it into an object of a class. Everything is working as expected but I'm unable to deallocate the dynamically allocated 2d array of objects.
I know the issue is within the destructor and the Move assignment operator for the object, I keep getting SIGBRT and EXC_BAD_ACCESS errors when I try to run it.
Below is my Code for the constructor, destructor and move assignment/constructor
//CustomerOrder.cpp
CustomerOrder::CustomerOrder(std::string&
src):Name(src),Product(),ItemCount(),ItemList(),field_width(){
std::vector<ItemInfo> info;
std::string* tokens[] = { &Name, &Product };
Utilities utils;
size_t next_pos = -1;
bool more = true;
for (auto& i : tokens) {
if (!more) break;
*i = utils.extractToken(src, next_pos, more);
}
while (more){
info.push_back(utils.extractToken(src, next_pos, more));
}
if(!info.empty() && info.back().ItemName.empty()){
info.pop_back();
}
ItemCount = info.size();
ItemList = new ItemInfo*[ItemCount];
for (int i = 0; i < ItemCount; i++){
ItemList[i] = new ItemInfo(info.at(i).ItemName);
}
if (utils.getFieldWidth() > field_width){
field_width = utils.getFieldWidth();
}
}
CustomerOrder::~CustomerOrder(){
for(int i = 0; i<ItemCount;i++){
delete[] ItemList[i];
}
delete[] ItemList;
}
CustomerOrder::CustomerOrder(CustomerOrder&& src){
*this = std::move(src);
}
CustomerOrder& CustomerOrder::operator=(CustomerOrder&& src){
if(this!= &src){
delete [] ItemList;
Name = std::move(src.Name);
Product = std::move(src.Product);
ItemCount = std::move(src.ItemCount);
ItemList = std::move(src.ItemList);
src.ItemList = nullptr;
}
return *this;
}
And the ItemInfo struct
//ItemInfo struct
struct ItemInfo
{
std::string ItemName;
unsigned int SerialNumber;
bool FillState;
ItemInfo(std::string src) : ItemName(src), SerialNumber(0),
FillState(false) {};
};
You are combining "new" with "delete[]". If you use "new" use "delete" if you use "new[]" then use "delete[]" for the thing.
This is your problem there: "delete[] ItemList[i];" it should be "delete ItemList[i];" instead
This line of your code ItemList[i] = new ItemInfo(info.at(i).ItemName); doesn't allocate a dynamic array, yet this code in your destructor tries to delete it as thought it was a dynamic array.
for(int i = 0; i<ItemCount;i++){
delete[] ItemList[i];
}
A quick fix would to be to change delete[] to delete. However, it appears as though it would be much easier to simply allocate a single dynamic array. In other words, allocate ItemList as such ItemList = new ItemInfo[ItemCount]; Granted, you would have to change the type, but it makes more sense from what you posted.
Another possible issue is that in your destructor you don't check if the ItemList is a nullptr or actually allocated to anything. To which, your destructor could possibly try to access invalid data. Not only that, but your move operator deletes the ItemList without deleting the data inside of it.
You could make a function to free up the data in ItemList and then call that function from the destructor and move operator.
On a side note, why are you using dynamic 2D arrays when it appears that you know how to use vectors? A vector would handle all of this in a much simpler fashion. For example, the type would be std::vector<std::vector<ItemInfo>>.
I have C++ code that uses raw pointer with C functions malloc, free and realloc.
I am thinking to change it to smart pointer, but I really want to keep the realloc functionality, because I believe it is much better than new, since it does not need to "move" the contents every time.
How I can refactor this with std::unique_ptr?
Here is some pseudo code (I realize this code is not 100% safe. In fact I never tried to compile it)
class Demo{
char *ptr;
Demo(size_t size) : ptr( malloc(size) ){}
bool resize(size_t size){
char *ptr_new = realloc(ptr, size);
if (ptr_new == nullptr){
return false; // fail
}
// here is tricky part with std::unique_ptr,
// ptr is already deleted...
ptr = ptr_new;
return true;
}
};
The way to reassign a pointer without deleting it is to use release():
auto old_ptr = unique.release();
unique.reset(new_ptr);
So in your code that would be something like:
struct free_deleter {
void operator(void* p) const { free(p); }
};
std::unique_ptr<char, free_deleter> ptr; // NB: malloc must be paired with free, not delete
bool resize(size_t size) {
char* ptr_new = realloc(ptr.get(), size);
if (!ptr_new) {
return false;
}
ptr.release();
ptr.reset(ptr_new);
return true;
}
Take the following Seat class:
class Seat
{
Passenger* passenger; // I'd like this to be Passenger passenger;
}
If I remove the asterisk, how should I update the following method?
bool initSeat()
{
passenger = NULL; // &passenger = NULL; does not compile as it's not a reference type.
return passenger == NULL; // Is there even a need to allocate memory? Maybe have the method blank?
}
and
bool insertSeat(Passenger* p)
{
bool bsuccess = TRUE;
if (p != NULL)
{
if (passenger == NULL) // replace with &passenger
passenger = p; // replacing with &passenger doesn't compile
else
bsuccess = FALSE;
}
else
passenger = NULL; // again, prefixing the & doesn't compile (that'd make it a reference type) so how do I set the pointer to NULL?
return bsuccess;
}
I might be confused because I am missing some basic concept.
Edited version. Read my comments:
class Seat
{
Passenger passenger;
}
InitSeat not required since passenger would be initialized by constructor of Passenger class.
//bool initSeat()
//{
// passenger = NULL;
//return passenger == NULL;
//}
Assuming, you would be getting a pointer only as argument:
bool insertSeat(Passenger* p)
{
bool bsuccess = TRUE;
if (p != NULL)
{
passenger = *p; // dereference the pointer directly
}
else {
passenger = Passenger(); // Might not be needed, but if needed make sure the assignment operator is properly(operator=) implemented
}
return bsuccess;
}
Basically you need to brush up pointer stuff and basic class designing.
You should really read more on pointers to help you understand memory allocation and dereferencing. I highly recommend you read some solid C/C++ beginners tutorials before continuing.
Basically if you have a pointer, it is just a memory address. You allocate memory and assign that address to the pointer. Then you use the pointer to reference memory appropriately.
If you are converting a member from a pointer to a regular variable, you would no longer need to allocate any memory. You would also access its members with a "." versus an "->" character. But keep in mind when you pass this variable around, it is now much larger (the size of all its members), which is why passing a pointer around is much more efficient.
It looks like from your comments that you are trying to use references... which is another topic. Try this:
Difference between pointer to a reference and reference to a pointer
There is no such thing as a null object. There are many ways of emulating this behaviour though. For example, you can overload the bool conversion operator for Passenger to achieve the behaviour you want. For example,
class Passenger
{
public:
operator bool() const{return not isNull;}
void nullify() {isNull = true;}
protected:
bool isNull;
};
initSeat would now look like this:
bool initSeat()
{
passenger.nullify();
return !passenger;
}
bool insertSeat(Passenger p)
{
bool bsuccess = TRUE;
if (p)
{
if (!passenger)
passenger = std::move(p);
else
bsuccess = FALSE;
}
else
passenger.nullify();
return bsuccess;
}
You could also try to use boost::optional.
vector<ClassX> xVec;
if (inputFile.peek() == '$')
{
classX classXInstance; //<==================== local instantiation
readFileElements(classXInstance);//<== pass by reference
if(classXInstance.validate())
{
xVec.push_back(classXInstance);///<=============== added here
}
/// destructor of the local copy is called here
}
I get a core dump, tried to debug, however I get so much junk messages with gdb, all I can see that the vector got corrupted, NOT sure if it because the destructor is called is a reason??
EDIT:
my class look like this
class ClassX
{
public:
ClassX() { numberOfX=0; ppXX = NULL; };
~ClassX();
void validate();
char **setX(const vector<string>& Xss);
inline char **getX() {return ppXX;};
private:
int numberOfX;
char **ppXX;
};
and it contains a destructor as follow
ClassX::~ClassX()
{
if (ppXX != NULL)
{
for(int i=0; i < numberOfXX; i++)
{
if (ppXX[i] != NULL)
{
delete [] ppXX[i];
ppXX[i] = NULL;
}
}
// Free array of pointers.
delete [] ppXX;
ppXX = NULL;
}
}
the setX allocate all memory necessary
validate give me a printout of the ppXX[i] and return true if number of elements matches the size of string vector
A copy of classXinstance is stored into xVec, with a pointer ppXX to a region in memory. Now you have two objects pointing to the same region. A moment later, classXinstance is destroyed, so the region is subject to delete. The element within xVec is now pointing to invalid memory.
The best option is to use std::Vector<std::string> instead of char **ppXX; a vector of strings takes care of references and allocation so you don't need to worry about proper construction/copy/destruction.