Qt undocumented method setSharable - c++

I stumbled about a method which seems to be present in all data objects like QList, QQueue, QHash...
I even investigated so far I can see the source code of it, which is
inline void setSharable(bool sharable) {
if (!sharable) detach(); d->sharable = sharable;
}
in qlist.h (lines 117).
But what effect does it have on the QList, QQueue, QHash... ? And is it in any way related to threading (which sounds reasonable)?
Thanks for any answer, and please only answer if you got actual knowledge.

No one could say more clear:
http://qt.nokia.com/doc/4.6/implicit-sharing.html
It is common practice to realize containers this way.

The sharable state you're asking about has nothing to do with mutlithreading. It is instead an implementation detail of copy-on-write data classes (even single-threaded ones) that hand out references to internal state.
Consider a class String that is implemented using CoW (for illustration purposes, this class isn't usable in threaded contexts, because accesses to d->refcount aren't synchronised, it also doesn't ensure that the internal char arrary ends in '\0', and might as well eat your grandmother; you have been warned):
struct StringRep {
StringRep()
: capacity(0), size(0), refcount(0), sharable(true), data(0) {}
~StringRep() { delete[] data; }
size_t capacity, size, refcount;
bool sharable; // later...
char * data;
};
class String {
StringRep * d;
public:
String() : d(new StringRep) { ++d->refcount; }
~String() { if (--d->refcount <= 0) delete d; }
explicit String(const char * s)
: d(new StringRep)
{
++d->refcount;
d->size = d->capacity = strlen(s);
d->data = new char[d->size];
memcpy(d->data, s, d->size);
}
String(const String &other)
: d(other.d)
{
++d->refcount;
}
void swap(String &other) { std::swap(d, other.d); }
String &operator=(const String &other) {
String(other).swap(*this); // copy-swap trick
return *this;
}
And a sample function each for mutating and const methods:
void detach() {
if (d->refcount == 1)
return;
StringRep * newRep = new StringRep(*d);
++newRep->refcount;
newRep->data = new char[d->size];
memcpy(newRep->data, d->data, d->size);
--d->refcount;
d = newRep;
}
void resize(size_t newSize) {
if (newSize == d->size)
return;
detach(); // mutator methods need to detach
if (newSize < d->size) {
d->size = newSize;
} else if (newSize > d->size) {
char * newData = new char[newSize];
memcpy(newData, d->data, d->size);
delete[] d->data;
d->data = newData;
}
}
char operator[](size_t idx) const {
// no detach() here, we're in a const method
return d->data[idx];
}
};
So far so good. But what if we want to provide a mutable operator[]?
char & operator[](size_t idx) {
detach(); // make sure we're not changing all the copies
// in case the returned reference is written to
return d->data[idx];
}
This naïve implementation has a flaw. Consider the following scenario:
String s1("Hello World!");
char & W = s1[7]; // hold reference to the W
assert( W == 'W' );
const String s1(s2); // Shallow copy, but s1, s2 should now
// act independently
W = 'w'; // modify s1 _only_ (or so we think)
assert( W == 'w' ); // ok
assert( s1[7] == 'w' ); // ok
assert( s2[7] == 'W' ); // boom! s2[7] == 'w' instead!
To prevent this, String has to mark itself non-sharable when it hands out a reference to internal data, so that any copy that is taken from it is always deep. So, we need to adjust detach() and char & operator[] like this:
void detach() {
if (d->refcount == 1 && /*new*/ d->sharable)
return;
// rest as above
}
char & operator[](size_t idx) {
detach();
d->shareable = false; // new
return d->data[idx];
}
When to reset the shareable state back to true again? A common technique is to say that references to internal state are invalidated when calling a non-const method, so that's where shareable is reset back to true. Since every non-const function calls detach(), we can reset shareable there, so that detach() finally becomes:
void detach() {
if (d->refcount == 1 && d->sharable) {
d->sharable = true; // new
return;
}
d->sharable = true; // new
StringRep * newRep = new StringRep(*d);
++newRep->refcount;
newRep->data = new char[d->size+1];
memcpy(newRep->data, d->data, d->size+1);
--d->refcount;
d = newRep;
}

Related

My vector works sometimes and othertimes it doesn't

I'm trying to make my own vector, but i've got the following problem: When I push_back 100 times there's no problem. When I push_back 1000 the program does not work
#include <iostream>
#include <stdlib.h>
#include <conio.h>
struct Exception {
static const char* out_of_range;
};
const char* Exception::out_of_range = "[Error]: Out of range";
template < typename T >
struct vector {
typedef T myType;
public:
vector() {
m_vector = (myType*) malloc ( sizeof( myType ) );
m_position = 0;
}
template < typename ... Ts >
vector(myType head, Ts ... tail) {
m_position = 0;
m_vector = (myType*) malloc( (sizeof ...( tail ) + 1) * sizeof( myType ) );
this->push_back(head);
(this->push_back(tail),...);
}
~vector() {
free(m_vector);
m_vector = NULL;
}
void push_back( myType value ) {
m_vector[ m_position ] = value;
++m_position;
m_vector = (myType*) realloc(m_vector, m_position * sizeof(myType));
}
void pop_back() {
--m_position;
m_vector = (myType*)realloc( m_vector, m_position * sizeof (myType) );
}
myType at( size_t pos ) {
try {
if (pos < m_position)
return m_vector[ pos ];
else throw Exception::out_of_range;
} catch (const char* e) {
printf("%s", e);
return (myType){};
}
}
inline myType& front() { return *m_vector; }
inline myType& back() { return *(m_vector + size() -1); }
inline myType* data() { return m_vector; }
inline myType* begin() { return m_vector; }
inline myType* end() { return (m_vector + size()); }
inline myType operator[](size_t pos) { return m_vector[ pos ]; }
inline size_t size() { return m_position; }
inline bool empty () { return (begin() == end()? true:false); }
private:
myType* m_vector;
size_t m_position;
};
Here is my main that use push_back by 100 times:
int main() {
vector<int> v;
for(int i = 0; i < 100; ++i) v.push_back(i);
for(int i = 0; i < 100; ++i) std::cout << v[i];
}
And here the hunted code ahah:
int main() {
vector<int> v;
for(int i = 0; i < 1000; ++i) v.push_back(i);
for(int i = 0; i < 1000; ++i) std::cout << v[i];
}
With "doesn't work" I'm trying to say that when I have 100 values inserted by push_back the program show me all the values from 0 to 99... but when I've got 1000 values (I don't know why) the program show only a black screen and nothing more
Consider the first call of
void push_back(myType value) {
m_vector[m_position] = value; // Store into 0
++m_position; // set `m_position` to 1
m_vector = (myType*)realloc(m_vector, m_position * sizeof(myType)); // Allocate more space.
}
How much more space is allocated on that last line? m_position * sizeof(myType). This resolves to 1 * sizeof(myType). Enough space for 1 myType. In other words the same amount of space the program already had. This is not useful.
Let's look at the next push_back
void push_back(myType value) {
m_vector[m_position] = value; // Store into 1. There is no 1. Program now broken
++m_position; // set `m_position` to 2
m_vector = (myType*)realloc(m_vector, m_position * sizeof(myType)); // Allocate more space.
}
The next push_back writes into invalid storage. Program now officially broken and no further point debugging.
How do we fix this?
Let's ignore the fact that malloc and family don't handle complex data structures and vector does not observe the Rules of Three and Five. Those are best handled in other questions. How do we fix this with realloc?
m_vector = (myType*) realloc(m_vector, (m_position +1) * sizeof(myType));
smooths over the immediate rough spot. But this is inefficient as hell. Every addition triggers a realloc. This really, really hurts performance. Aggregate O(1) goes right out the window replaced by O(n), copy every time, plus a potentially very expensive memory allocation.1
Worse, what happens when you remove an item? You lose track of how much was in the vector and may find yourself reallocing smaller buffers. Yuck.
To do this right, first add a m_capacity member to track how much data can be stored so that we don't have to reallocate if the amount needed is less than the amount required.
Then we test for amount of space and possibly reallocate before trying to store.
void push_back( myType value ) {
if (m_position >= m_capacity)
{ // need to reallocate
m_capacity *= 2;
myType * temp = (myType*) realloc(m_vector, m_capacity *sizeof(myType));
// ask for more than is needed. Reduce number of reallocations needed
// do not overwrite m_vector. realloc can fail to allocate and then where are you?
if (temp != NULL)
{
m_vector = temp;
}
else
{
// handle error. Probably throw exception. Definitely exit function
// before trying to add new element
}
}
m_vector[ m_position ] = value; // now guarantied to have space.
++m_position;
}
1This isn't strictly true. One of the things you'll find is that memory provided often isn't as granular as what you asked for. When the program asks for X bytes, it might get a convenient block of free memory larger than X bytes. You ever noticed that sometimes you can run off the end of a buffer and the program doesn't notice and immediately crash? This extra space is one of the reasons. Quite often realloc can take advantage of this and keep using the same allocation over and over, allowing the program to legally see more of it. You can't count on this, though.
I assume the idea behind your code is that m_vector should always be able to hold one more value than it currently does. Your push_back funtion is wrong then, it should realloc for m_position + 1.

Implementing a reference counter with a limit of 3 pointers

**Update added main and cat
Hello all i am creating a sharedpointer class that has a reference counter.
What im trying to do is when i create a new object and give it value i can do so until i reach a cap of 3.
If i create anymore objects they will get NULL if i try to use obj.getPointer().
Currently when i try and create objects i get 0x22bd730 when i console log my main :(
If you want to restrict to 3 references, your copy constructor and assignment should correctly act once limit is reached, something like:
sharedPTR(const sharedPTR<T>& ref) : myData(nullptr), myRef(nullptr)
{
if (ref.myRef && ref.myRef->getCount() < 3) {
myData = ref.myData;
myRef = ref.myRef;
myRef->AddRef();
}
}
sharedPTR<T>& operator = (const sharedPTR<T>& ref)
{
if (this == &ref) {
return *this;
}
if (myRef && myRef->Release() == 0)
{
delete myData;
delete myRef;
}
myData = nullptr;
myRef = nullptr;
if (ref.myRef && ref.myRef->getCount() < 3) {
myData = ref.myData;
myRef = ref.myRef;
myRef->AddRef();
}
return *this;
}
Demo

No Appropriate Default Constructor Available despite default constructor made?

Trying to make my own Map struct to store my own-created 'Strings,' and after 8 hours or so finally got it down to only a few compiler errors (six of them). I've spent the last hour and forty minutes searching the web for answers, only to find people forgot default constructors, and tried mixing things up in my own program. Since I'm not really sure where the problem is in advance, I apologize for posting all this code...I put what I thought were the most relevant files first; I think only the first 3 are necessary. The error is
"SubdomainPart' : No appropriate default constructor available" for lines 12 and 20 of the Map.h file.
Map.h
// Map.h - Map template class declaration
// Written by -----
#pragma once
template<typename KEY_TYPE, typename VALUE_TYPE>
struct Map
{
public:
// Default / initial constructor hybrid
Map(int initialCapacity = 10)
{
Size = 0;
Capacity = initialCapacity;
Key;
MappedValue;
//Allocate the C-Array elements using HEAP
Data = new VALUE_TYPE[Capacity];
}
struct iterator
{
KEY_TYPE * current;
KEY_TYPE * prev;
KEY_TYPE * next;
iterator operator ++ ()
{
iterator it = this;
iterator itNext = it.next;
it.next = itNext.next; // pushes iterator forward.
it.prev = it.current;
it.current = it.next;
}
iterator operator -- ()
{
iterator it = this;
iterator itPrev = it.prev;
it.prev = itPrev.prev; // pushes iterator backward.
it.next = it.current;
it.current = it.prev;
}
};
Map(const Map& copyFrom)
{
// Necessary to prevent the delete[] Data; statement in the assignment operator from
// freezing because Data has some garbage address in it.
Data = NULL;
*this = copyFrom; //'this' points to the current instance of the object. (in this case, 'Map')
}
// Destructor: MUST HAVE because we allocate memory
~Map()
{
delete[] Data;
}
Map& operator = (const Map& copyFrom)
{
// 0) delete the old one!
delete[] Data;
// 1) copy Size and Capacity
Size = copyFrom.Size;
Capacity = copyFrom.Capacity;
// 2) Allocate Memory
Map* Data = new Map[Capacity];
// 3) Copy the Map Elements
for(int i = 0; i<Size; i++)
Data[i] = copyFrom.Data[i];
return *this;
}
// Index Operator
VALUE_TYPE& operator[] (KEY_TYPE key) const
{
return Data[key];
}
// Accessor functions: read-only access to Size and Capacity
int GetSize() const //const does not modify ANY data members of the class (size, capacity, or data)
{
return Size;
}
int GetCapacity() const
{
return Capacity;
}
void PushBack(const VALUE_TYPE& newElement) //adds value to end of Map as default
{
if(Size >= Capacity)
increaseCapacity(2 * Capacity);
Data[Size] = newElement;
Size++; // increases size of the array so it can be used later.
}
// Overloaded Add function, inserts a value at specified index, calls in "Insert" to do so.
void Add(const VALUE_TYPE& newElement, int index)
{
if( (index<0) || (index > Size))
{
throw ("Index to insert is out of range");
}
//Make sure there's space!
if (Size >= Capacity)
increaseCapacity(2*Capacity); //increase size of array if too small!
Insert(index, newElement);
}
void Remove(int index) // index = index to be removed.
{
// Make sure it's inside the bounds
if( (index<0) || (index > Size))
{
throw ("Index to Remove is out of range.");
}
// it's going to remove the unneeded space by having its capacity one above the Size.
Map* new_Data = new Map[Size];
//Copy data onto new pointer section.
for(int x = 0; x<Size; x++)
new_Data[x] = Data[x];
delete[] Data; //deallocates old memory and uneeded capacity slots.
for(int x = index; x < (Size - 1); x++) //removes the value at index 'index.' Now Data has a capacity of the amount of slots used and one more for a NULL value.
new_Data[x] = new_Data[x+1];
Data = new_Data;
Data[Size-1] = NULL;
Size--;
}
void increaseCapacity(int new_capacity)
{
if(new_capacity>Capacity)
{
if(new_capacity> 2* Capacity)
Capacity = new_capacity;
else
Capacity *= 2;
//create Map with a new capacity!
Map* new_Map = new Map[Capacity];
for(int x = 0; x<Size; x++)
{
new_Map[x] = Data[x];
}
//clear out old memory
delete[] Data;
//set data pointer to the new Map
Data = new_Map;
}
}
KEY_TYPE * Key; // Used to identify mapped values.
VALUE_TYPE MappedValue; // The value actually contained.
private:
int Size; // The count of actual C-Array elements used
int Capacity; // The count of C-array elements allocated
// The encapsulated C-array
VALUE_TYPE * Data; // pointer of type 'DATA_TYPE' called data (will be name of our array).
void Insert(const int index, const VALUE_TYPE& insertValue)
{
if( (index<0) || (index > Size))
{
throw out_of_range ("Index to insert is out of range");
}
//Time to shuffle the array down!
for(int x = Size; x>index; x--)
{
Data[x] = Data[x-1];
}
//Insert the new item at index 'Index!'
Data[index] = insertValue;
Size++;
}
};
SubdomainPart.h
// SubdomainPart.h - SubdomainPart validation class declaration
// Written by -------
#pragma once
#include "String.h"
using namespace std;
class SubdomainPart
{
public:
// Takes the address and stores into the Address data member
SubdomainPart(const String& address);
// Returns true when the Address is valid or false otherwise
virtual bool IsValid();
private:
String Address;
};
SubdomainPart.cpp
// SubdomainPart.cpp - Subdomain validation class implementation
// Written by ---------
#pragma once
#include "SubdomainPart.h"
// Takes the address and stores into the Address data member
SubdomainPart::SubdomainPart(const String& address)
{
Address = address;
}
// Returns true when the Address is valid or false otherwise
bool SubdomainPart::IsValid()
{
int currentDotIndex = 0;
int nextDotIndex = 0;
int found = 0; // first index of a found invalid character
int hyphenIndex = 0; // used to check hyphen rule
// 1. Check the size, 255 total characters
if(Address.GetLength() < 1 || Address.GetLength() > 255)
return false;
// Checks for valid amount of 1-63 characters between dots
currentDotIndex = Address.FindFirstOf('.');
if(currentDotIndex == 0 || currentDotIndex == Address.GetLength()-1)
return false;
else if(currentDotIndex!=(-1))
nextDotIndex = Address.Find('.', currentDotIndex+1);
else
nextDotIndex = (-1); // if no '.' is found, ensures the following loop doesn't run.
while(nextDotIndex!=(-1))
{
if((nextDotIndex-currentDotIndex) == 1 || (nextDotIndex-currentDotIndex) > 63)
return false;
currentDotIndex = nextDotIndex;
nextDotIndex = Address.Find('.', currentDotIndex+1);
}
// 2. Check for valid characters
found = Address.FindFirstNotOf("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-.");
if(found!=(-1)) // if a character not listed above is found.
return false;
// 3. Check for dash rule
// Making sure hyphens aren't located at the first or last index of a subdomain.
hyphenIndex = Address.FindFirstOf('-');
if(hyphenIndex == 0)
return false;
hyphenIndex = Address.FindLastOf('-');
if(hyphenIndex == Address.GetLength()-1)
return false;
// Makes sure two hyphens aren't in a row.
for(int x = 1; x<Address.GetLength(); x++)
if(Address[x] == '-' && Address[x] == Address[x-1])
return false;
return true;
}
I don't see a default constructor in this class:
class SubdomainPart
{
public:
// Takes the address and stores into the Address data member
SubdomainPart(const String& address);
// Returns true when the Address is valid or false otherwise
virtual bool IsValid();
private:
String Address;
};
Keep in mind that this map constructor is default-constructing every member rather than initializing them:
Map(int initialCapacity = 10)
{
Size = 0;
Capacity = initialCapacity;
Key;
MappedValue;
//Allocate the C-Array elements using HEAP
Data = new VALUE_TYPE[Capacity];
}
You don't have a default constructor for SubdomainPart you have only provided a copy constructor. A default constructor takes no argument.
The compiler is complaining that SubdomainPart doesn't have a default constructor, and indeed it doesn't. It's required because your Map contains an object of type VALUE_TYPE:
VALUE_TYPE MappedValue;
Also, your Map constructor contains very weird code. I assume you actually wanted to use an initialiser list:
Map(int initialCapacity = 10)
: Key()
, MappedValue()
, Size(0)
, Capacity(initialCapacity)
, Data(new VALUE_TYPE[Capacity])
{}
The problem is with Data = new VALUE_TYPE[Capacity]; part.
The compiler generates code to allocate the array and instantiate each element by calling the parameterless constructor for VALUE_TYPE. As SubdomainPart doesn't have one (since you have defined a custom one), the compiler throws an error.
The reason that compiler reports error in map.h is that it is exactly the place where the constructor is called from. It is not used in SubdomainPart code, it is just defined there.

Debug Assertion Failed … _BLOCK_TYPE_IS_VALID(pHead->nBlockUse)

I have got a really bad memory leak I am trying to fix, but somehow i am not able to delete Objects without triggering this assertation.
I have searched for a solution via Google and have read the Questions on stackoverflow about this Error but I was still not able to find the answer!
Possible reasons to get this Error according to my research:
1. deleting objects more then one
2. shadow copying
3. creating and deleting Objects that are loaded from an external dll
4. creating objects without storing the pointer
BUT:
1. I checked the code and was not able to find double deletion
2. I use a copy constructor to copy Objects
3. The Error relatet classes are build (with MS Visual Studio) to a seperate lib but not to a dll. AND all the classes that are related to this error are located in the same lib.
4. I checked the code and it seems like that's not the problem
It would be great if anybody is able to spot the mistake in the code below, and I appreciate every hint that points me to the solution of the problem.
EDIT:
I forgot to mention the same deleting problem in sendThreadMain of MessageSystem (see code below). If i delete the Message there it causes unexpected errors somewhere else in the code. Might just be wrong data transmission... but i do not really know.
This code is run on Windows and Linux!
Here are the error related parts of the code:
Message
class Message
{
public:
Message (char type, unsigned char id, unsigned short size)
{
mType = type;
mId = id;
mSize= size;
}
Message(const Message &o)
{
mType = o.mType;
mId = o.mId;
mSize = o.mSize;
}
char getType() const {return mType;};
unsigned char getId() const {return mId;};
unsigned short getSize() const {return mSize;};
protected:
char mType;
unsigned char mId;
unsigned short mSize;
};
class JoinMessage : public Message
{
public:
JoinMessage () : Message ('j', 0, sizeof (JoinMessage))
{
team = TEAM_SPECTATOR;
}
JoinMessage (unsigned char id) : Message ('j', id, sizeof (JoinMessage)){}
JoinMessage (const JoinMessage &o) : Message (o)
{
team = o.team;
setName(o.getName());
}
void setName(std::string newName)
{
if (newName.length() > MAX_PLAYER_NAME_LENGHT)
newName = newName.substr(0, MAX_PLAYER_NAME_LENGHT);
memset(name, 0, MAX_PLAYER_NAME_LENGHT);
for(unsigned int i = 0; i < newName.length(); i++)
name[i] = newName[i];
}
std::string getName() const
{
std::string stringToReturn;
for(unsigned int i = 0; i < MAX_PLAYER_NAME_LENGHT; i++)
{
if (name[i])
stringToReturn.push_back(name[i]);
else
break;
}
return stringToReturn;
}
TeamIdentifier team;
private:
unsigned char name[MAX_PLAYER_NAME_LENGHT];
};
// there are a lot other messages
MessageQueue
MessageQueue::~MessageQueue()
{
boost::mutex::scoped_lock lock (queueMutex);
while(messageQueue.size() > 0)
{
// the crash is non-reproducible
// works 90% of the time
delete messageQueue.front (); // <- Debug Assertion Failed … _BLOCK_TYPE_IS_VALID
messageQueue.pop_front();
}
}
void MessageQueue::enqueMessage (Message* message)
{
{
boost::mutex::scoped_lock lock (queueMutex);
messageQueue.push_back(message);
}
}
Message* MessageQueue::dequeMessage ()
{
boost::mutex::scoped_lock lock (queueMutex);
if (messageQueue.size() == 0)
return nullptr;
Message* message = messageQueue.front ();
messageQueue.pop_front();
return message;
}
MessageSystem
template <class MessageType>
void broadcast (MessageType &message)
{
MessageType *internMessage = new MessageType(message);
boost::mutex::scoped_lock lock (mRecipientMapMutex);
std::map <boost::asio::ip::udp::endpoint, MessageQueue *>::iterator it;
for (it = mRecipientMap.begin ();
it != mRecipientMap.end ();
it++)
{
it->second->enqueMessage(internMessage);
}
}
template <class MessageType>
void post (MessageType &message, boost::asio::ip::udp::endpoint &recipient)
{
MessageType *internMessage = new MessageType(message);
std::map <boost::asio::ip::udp::endpoint, MessageQueue* >::iterator it;
MessageQueue *messageQueue = NULL;
{
boost::mutex::scoped_lock lock (mRecipientMapMutex);
it = mRecipientMap.find (recipient);
if (it != mRecipientMap.end())
messageQueue = it->second;
if(messageQueue)
messageQueue->enqueMessage (internMessage);
}
}
void MessageSystem::sendThreadMain ()
{
// copy endpoints to vecotr so it can be
// deleted from map while iterating
std::vector<udp::endpoint> endpoints;
{
boost::mutex::scoped_lock lock (mRecipientMapMutex);
std::map <udp::endpoint, MessageQueue *>::iterator mapIt = mRecipientMap.begin ();
while (mapIt != mRecipientMap.end())
{
endpoints.push_back(mapIt->first);
mapIt++;
}
}
std::vector<udp::endpoint>::iterator endpointIt = endpoints.begin();
while (endpointIt != endpoints.end())
{
char sendBuffer[PACKET_SIZE];
int sendBufferPosition = 0;
{
boost::mutex::scoped_lock lock (mRecipientMapMutex);
MessageQueue *messageQueue = mRecipientMap[*endpointIt];
if (messageQueue == nullptr)
{
mRecipientMap.erase(*endpointIt);
endpointIt++;
continue;
}
while (Message *message = messageQueue->dequeMessage ())
{
if (sendBufferPosition + message->getSize() > PACKET_SIZE)
{
// put message back and send it later
messageQueue->enqueMessage (message);
break;
}
// copy message into buffer
std::memcpy (
&sendBuffer [sendBufferPosition], message, message->getSize());
sendBufferPosition += message->getSize();
// deleting this message causes a crash if 2 or more
// recipients are registered within MessageSystem
//delete message; <- RANDOM CRASH elsewhere in the program
}
}
.... // more code down here that seems not related to the error
Today I figured it out on my own. It was #1 of the 4 possibilities mentioned in the Question.
deleting objects more then once (by saving multiple pointers to the exact same object)
Here is my Solution in MessageQueue:
template <class MessageType>
void broadcast (MessageType &message)
{
// I was creating 1 new Message right here but I need 1 new Message
// in EVERY MessageQueue so i moved the next line ...
// MessageType *internMessage = new MessageType(message);
boost::mutex::scoped_lock lock (mRecipientMapMutex);
std::map <boost::asio::ip::udp::endpoint, MessageQueue *>::iterator it;
for (it = mRecipientMap.begin ();
it != mRecipientMap.end ();
it++)
{
// ... down here. Now every queue contains its own copy of the Message
MessageType *internMessage = new MessageType(message);
it->second->enqueMessage(internMessage);
}
}
It might be a simple problem of wrong order. You are doing:
while(messageQueue.size() > 0)
{
delete messageQueue.front();
messageQueue.pop_front();
}
Maybe deleting the message after popping it, instead of before, would do the trick:
while(messageQueue.size() > 0)
{
Message* pFront = messageQueue.front();
messageQueue.pop_front();
delete pFront;
}
Anyway, I am not confident at all on this solution, since deleting the object pointed by pFront should have no effect on the queue itself, which just stores pointers. But you can try.
Well, I faced similar problem,
the following code
Message* message = messageQueue.front ();
messageQueue.pop_front();
return message;
The code that produced error with me was:
Point *p = q.LookFor(&q, &pts[5], &Dist);
cout ...
delete p;
It seems that the function delete the pointer it creates in the runtime, so you're not allowed to delete it "again"
so I replaced it with
Point p = *(q.LookFor(&q, &pts[5], &Dist));
and it's gone.

strlen() not working

Basically, I'm passing a pointer to a character string into my constructor, which in turn initializes its base constructor when passing the string value in. For some reason strlen() is not working, so it does not go into the right if statement. I have checked to make sure that there is a value in the variable and there is.
Here is my code, I've taken out all the irrelevant parts:
Label class contents:
Label(int row, int column, const char *s, int length = 0) : LField(row, column, length, s, false)
{
}
Label (const Label &obj) : LField(obj)\
{
}
~Label()
{
}
Field *clone() const
{
return new Label(*this);
}
LField class contents:
LField(int rowNumVal, int colNumVal, int widthVal, const char *valVal = "", bool canEditVal = true)
{
if(strlen(valVal) > 0)
{
}
else
{
//This is where it jumps to, even though the value in
//valVal is 'SFields:'
val = NULL;
}
}
Field *clone() const
{
return new LField(*this);
}
LField(const LField &clone) {
delete[] val;
val = new char[strlen(clone.val) + 1];
strcpy(val, clone.val);
rowNum = clone.rowNum;
colNum = clone.colNum;
width = clone.width;
canEdit = clone.canEdit;
index = clone.index;
}
Screen class contents:
class Screen {
Field *fields[50];
int numOfFields;
int currentField;
public:
Screen()
{
numOfFields = 0;
currentField = 0;
for(int i = 0; i < 50; i++)
fields[i] = NULL;
}
~Screen()
{
for (int i = 0; i < 50; i++)
delete[] fields[i];
}
int add(const Field &obj)
{
int returnVal = 0;
if (currentField < 50)
{
delete[] fields[currentField];
fields[currentField] = obj.clone();
numOfFields += 1;
currentField += 1;
returnVal = numOfFields;
}
return returnVal;
}
Screen& operator+=(const Field &obj)
{
int temp = 0;
temp = add(obj);
return *this;
}
};
Main:
int main () {
Screen s1;
s1 += Label(3, 3, "SFields:");
}
Hopefully someone is able to see if I am doing something wrong.
<LANGUAGE FEATURE XXXX IS BROKEN>! ... No, it isn't.
Just before measuring the string, write in a puts(valVal), to ensure you are not mistaken about the contents of that variable.
Marcin at this point the problem will come down to debugging, I copied your code with some minor omissions and got the correct result.
Now it needs to be said, you should be using more C++ idiomatic code. For instance you should be using std::string instead of const char* and std::vector instead of your raw arrays.
Here is an example of what the LField constructor would look like with std::string:
#include <string> // header for string
LField(int rowNumVal,
int colNumVal,
int widthVal,
const std::string& valVal = "",
bool canEditVal = true)
{
std::cout << valVal;
if(valVal.length() > 0)
{
}
else
{
//This is where it jumps to, even though the value in
//valVal is 'SFields:'
//val = NULL;
}
}
Using these types will make your life considerably easier and if you make the change it may just fix your problem too.
PREVIOUS:
So you can be CERTAIN that the string is not being passed in correctly add a printline just before the strlen call. Once you do this work backward with printlines until you find where the string is not being set. This is a basic debugging technique.
Label(int row,
int column,
const char *s,
int length = 0) :
LField(row, column, length, s, false) {
}
LField(int rowNumVal,
int colNumVal,
int widthVal,
const char *valVal = "",
bool canEditVal = true)
{
std::cout << valVal << std::endl;
if(strlen(valVal) > 0)
{
}
else {
//This is where it jumps to, even though the value in
//valVal is 'SFields:'
val = NULL;
}
}
Whenever there is strange behavior like this, memory getting screwed up is almost always the culprit. Never mix new with delete[] OR new[] with delete. The latter is slightly worse than the former but they are both bad news. delete[] should only be used in conjunction with new[]. Mixing these allocation/deallocation notations will result in undefined behavior. Since you are never using new[], replace all of your delete[] calls with delete. It is also good practice and good manners to set your pointers to NULL after you delete them. It is highly unlikely that you will be the only one debugging this code and it would be extremely annoying to debug your pointers thinking that there is valid memory there, when in fact there isn't.
Mixing these notations inevitably lead to exploits and memory leaks.
There is a problem here:
LField(const LField &clone) {
delete[] val;
val = new char[strlen(clone.val) + 1];
val is uninitialized when the constructor is called, and you are deleting it.