• Advertisement

Archived

This topic is now archived and is closed to further replies.

ptr to dynamic array lifetime and scope

This topic is 5755 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Ok I am a bit confused with lifetime of arrays (created using new). I will attempt to explain what I am confused about... Ok here goes, I have a stuct I use to hold messages which can contain messages of varible length:
    struct DArray
{
char *p_Array;
int ArraySize;
}  
I use new operator to make char array of required size and store size of array in ArraySize member. I then fill up array with data. I then add the struct instance to a std::queue of type:
  std::queue< DArray, std::list<DArray> >ArrayQueue;

void AddDArray(void)
{
DArray TestArray;
TestArray.p_Array = new char[10];
TestArray.ArraySize = 10;
// code to fill in array...

ArrayQueue.push(DArray)
}

/* DArray.ArraySize still contains correct value, ptr still points to right place but array created using new has be reclaimed!!*/
    
My problem is that the array pointed to by DArray.p_Array seems to be getting deallocated as soon as the function which created it exits but the rest of the DArray is fine (DArray has been added to the queue so everything but the data pointed to by the ptr is fine) is there any way to prevent the array being reclaimed? I have had a read through my C++ books but they don't seem to cover this it mentions the static keyword but I don't think this is what I am after. Any suggestions anyone? I hope that makes sense... Cheers, Convict@Large "I code therefore I am" Anon [edited by - convict@large on April 18, 2002 12:07:17 PM]

Share this post


Link to post
Share on other sites
Advertisement
What makes you suspect the array is being deallocated? Anything that you allocate using new will be deallocated when you delete it, but not before. But, wait a minute! You have a queue of DArrays and you are adding a DArray to the queue, but you haven''t defined a copy constructor to your DArray class. That means what happens is this:

(I''m going to assume that you remember to delete the dynamically allocated array in DArray''s destructor)

1. You have a DArray containing a pointer member (let''s call it "da";

2. You push da onto the queue. This creates a copy of da, and it is the copy that is stored in the queue, not the original. Since you did not define a copy constructor for the class, the compiler writes one for you. The default copy ctor performs a memberwise copy of the object. For the pointer member, the value of the pointer is copied (i.e. the address it contains), but the object it points to is not copied. This is known as "shallow copy".

3. You now have two objects pointing at your dynamically allocated array: da, and the copy of da.

4. one of the two objects goes out of scope, and is deleted. The destructor of DArray ensures the dynamically allocated array is deleted.

5. You now have one of the "da" objects pointing to storage that has been deleted. Whoops!

I suggest that the solution to your problem lies in using std::string instead of a char* member. std::string will allow you to store strings of char type, and will do the right thing when you copy objects around. It will also allow you to drop the ArraySize member.


[C++ FAQ Lite | ACCU | Boost | Learning C++]

Share this post


Link to post
Share on other sites
hmm thanks for that SabreMan. I am using the char p_Array to store data to be sent over a network using UDP Winsock socket. To send data using winsock you must supply a char* and the size of the buffer point to by char*. I see there is a size() member and a data() member for string...

Can I use the basic_string.data() member as the char* required for winsock does the pointer returned by basic_string.data() point to the whole array of size basic_string.size()? Sorry for asking such a simply question I will try to read up more on STL tonight....

Cheers,

Convict@Large

"I code therefore I am" Anon

[edited by - convict@large on April 18, 2002 12:21:28 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by Convict@Large
Can I use the basic_string.data() member as the char* required for winsock does the pointer returned by basic_string.data() point to the whole array of size basic_string.size()?

Yes, since the internal storage of string is guaranteed to be contiguous. It should work quite well with winsock. Just be aware that the returned pointer points to data that still belongs to the string, so you must not attempt anything that directly manipulates the data via the pointer.

quote:

Sorry for asking such a simply question I will try to read up more on STL tonight....

I''m not so sure it''s *that* obvious, but I''m pretty sure std::string will do what you want.


[C++ FAQ Lite | ACCU | Boost | Stroustrup on Learning C++]

Share this post


Link to post
Share on other sites
Just been doing some reading on copy constructors if I used this version of the struct DArray would it solve my array memory problem?

  struct DArray{
// default constructor

DArray();
//copy constructor

DArray(const DArray &o);
// destructor deletes array automatically

~DArray(void) { delete []p_Array;}
// ptr to array

char *p_Array;
// size of array

int ArraySize;
}


// define default constructor

DArray::DArray(void)
{
ArraySize = 0;
}


// define copy constructor?

DArray::DArray(const DArray &o)
{
ArraySize = o.ArraySize;
// allocate array size

p_Array = new char[ArraySize];
// copy contents

strcpy(p_Array, o.p_Array);
}

I supose I could also define a constructor to create array of set size as well. If the above code is correct and will prevent the array memory problem then I think I will use that as it as:
1) It will not require rewriting allot of code in my current project.
2) It provides the specfic functionality I need where as std::string has tonnes of functionality that I will never use.
3) I understand how this works and implementing it will further my understanding of C++.

Cheers,

Convict@Large

"I code therefore I am" Anon

[edited by - convict@large on April 18, 2002 12:20:44 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
You''re wasting CPU time by even making copies of your data. Instead of making an queue of DArray''s you should just make a queue of DArray pointers:

DArray* ArrayPtr = new DArray;
ArrayPtr->p_Array = new char[10];
ArrayPtr->ArraySize = 10;
// code to fill in array...
ArrayPtrQueue.push(ArrayPtr);


Now no more copy. Much better.

Share this post


Link to post
Share on other sites
I'm getting something strange happening when using a std::queue of DArray's that use'es (default and copy) constructors & destructors.

      
void func(void)
{
DArray Test;
if(Queue.size()>0)
{
// get front of queue

Test = Queue.front(); // is this causing copy constructor to be

// called or is it just a pointer?

// do some work with Test

// pop off front of queue

Queue.pop(); // right after this command data in Test.p_Array

//disappears or is freed/deallocated why is this happening?

} // I am getting "Invalid Address specified to RtlValidateHeap(

//310000, 318428 )" error here, which makes me think that the

//DArray destructor is trying to delete Test.p_Array apart from

//Test.p_Array has allready been deleted by Queue.pop()! Hence

//the error. Why is Queue.pop(); freeing Test.p_Array?*/

}

Anyone know why Queue.pop() is causing Test.p_Array to be deallocted(deleted)?

Cheers,

Convict@Large

"I code therefore I am" Anon

[edited by - convict@large on April 19, 2002 10:59:43 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by SabreMan
You also need an assignment operator for the class.

Ah is that the ''='' operator by any chance? So I overload the = operator so that is copies the data between them?

quote:
I probably should have mentioned that at the same time as the copy ctor. I still think you should look into using std::string.

Don''t worry about it. std::string is nice but I prefer to write my own as my struct/class is very simple and I think I learn more about C++ and STL if I write my own stuff.



Cheers,

Convict@Large

"I code therefore I am" Anon

Share this post


Link to post
Share on other sites
quote:
Original post by Convict@Large
std::string is nice but I prefer to write my own as my struct/class is very simple and I think I learn more about C++ and STL if I write my own stuff.

How can you learn more about the STL if you ignore what it provides? (Oxymoron, ''innit?)

Using std::string in no way jeapordizes your learning about dynamic arrays and what not - there are still uses for them outside of character array string emulation. Use std::string and std::vector because they are C++. char * and other forms of dynamic arrays are the C way to do things. And when you really need to do things the C way, use a smart pointer like std::auto_ptr or boost::smart_ptr.

[ GDNet Start Here | GDNet Search Tool | GDNet FAQ ]
[ MS RTFM [MSDN] | SGI STL Docs | Boost ]
[ Google! | Asking Smart Questions | Jargon File ]
Thanks to Kylotan for the idea!

Share this post


Link to post
Share on other sites
Sorry Oluseyi I do not mean to spurne string/STL or whatnot for the sake of it. I am on a deadline atm to get a demo done for a milestone and I allready have a struct similiar to DArray which is almost working (only needing an assignment operator added by the sounds of it). If I add the assignment operator then I should have it working and then I can get on with trying to meet the deadline.

To recode the engine to use std::string would take too long atm so I will continue to use my bodged struct until after the review. I will look at the situation then and decided what to do (use std::string or not) then.

No offence was ment.

Cheers,

Convict@Large

"I code therefore I am" Anon

Share this post


Link to post
Share on other sites
quote:
Original post by Convict@Large
Ah is that the ''='' operator by any chance? So I overload the = operator so that is copies the data between them?

Yup. Note, though that operator=() can be implemented in terms of the copy ctor and the std::swap() function, if you employ a few nifty tricks.

Here''s a demonstration of those nifty tricks:


  
class String
{
public:
String() : rep_(0) {}
String( const String& other )
{
setString(other.rep_);
}
explicit String( char *string )
{
setString( string );
}

String& operator=( String& other )
{
String temp(other);
Swap(temp);
return *this;
}

~String()
{
delete [] rep_;
}

const char* c_str() const
{
return rep_;
}

private:
void setString( const char *string )
{
rep_ = new char[strlen(string) +1];
strcpy(rep_,string);
}

void Swap( String& other )
{
std::swap(rep_, other.rep_);
}

private:
char* rep_;
};


The "nifty" tricks can be seen in the private helper functions. setString() is a helper for both copy and conversion ctors, and Swap is a helper for the assignment operator. You might like to ponder on what the assignment operator is doing and why.

I''m now going to issue the traditional disclaimer that this is not a class I use in my own code, it''s merely presented for exposition purposes.


[C++ FAQ Lite | ACCU | Boost | Stroustrup on Learning C++]

Share this post


Link to post
Share on other sites
quote:
Original post by Oluseyi
How can you learn more about the STL if you ignore what it provides? (Oxymoron, ''innit?)

Well, he *is* learning about the demands that the STL makes on a contained type, which he''ll need to learn at some point in using the STL.


[C++ FAQ Lite | ACCU | Boost | Stroustrup on Learning C++]

Share this post


Link to post
Share on other sites
quote:
Original post by SabreMan
Yes, since the internal storage of string is guaranteed to be contiguous. It should work quite well with winsock. Just be aware that the returned pointer points to data that still belongs to the string, so you must not attempt anything that directly manipulates the data via the pointer.


Correct me if I'm wrong, but I'm pretty sure that std::string's internal storage is NOT required to be contiguous (which is why you'd probably use c_str() instead of data() for retrieving a C-style string).

[edited by - kvh on April 19, 2002 12:04:49 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by SabreMan


      
String& operator=( String& other )
{
String temp(other);
Swap(temp);
return *this;
}

quote:
You might like to ponder on what the assignment operator is doing and why.


I am wondering why you are swapping contents of this._rep with temp._rep why not just copy temp._rep into this._rep? I looked in MSDN and it gave this example for operator=:

    
class Point
{
public:
Point &operator=( Point & ); // Right side is the argument.

...
};

// Define assignment operator.

Point &Point::operator=( Point &ptRHS )
{
_x = ptRHS._x;
_y = ptRHS._y;

return *this; // Assignment operator returns left side.

}

MSDN does not seem to swap values, I am confused as to why you are doing this...

And yes I like to figure out how code works before I use it :D

Cheers,

Convict@Large

[edited by - convict@large on April 19, 2002 11:58:21 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by kvh
Correct me if I''m wrong, but I''m pretty sure that std::string''s internal storage is NOT required to be contiguous

Yes, I didn''t state that very well. Internally, std::string does not have to store it''s data contiguously. However, the c_str() and data() member functions always return a pointer to a contiguous array.


[C++ FAQ Lite | ACCU | Boost | Stroustrup on Learning C++]

Share this post


Link to post
Share on other sites
Ah, thanks for pointing that out.

I thought data() was similar to &str[0], since MSDN told me:

quote:

The member function returns a pointer to the first element of the sequence (or, for an empty sequence, a non-null pointer that cannot be dereferenced).



but the SGI docs prove you right:

quote:

Returns a pointer to an array of characters, not necessarily null- terminated, representing the string''s contents. data() is permitted, but not required, to be identical to c_str(). The first size() characters of that array are guaranteed to be identical to the characters in *this. The return value of data() is never a null pointer, even if size() is zero.


Share this post


Link to post
Share on other sites
I think using std::string as a buffer for the wire is gross misuse.

I assume you''re doing asyncronous IO and/or using threads to send the data (otherwise there''s not much point in building a packet queue)
What you really want is a circular memory pool, and I like adding stream operators. That way the packets are serialized once, and never copied after that. There is scatter/gather IO which you could use to prevent any copies, but it means you have to maintain the lifetime of any object pointed to by the sgio and it makes applying encryption and/or compression difficult as you would need sg versions of those functions and they would trash the original items. So you kinda need one copy anyway.


  
template<typename T>
inline StreamBuffer& operator<<(StreamBuffer& stream, const T& lhs)
{
StreamBuffer::tyBuffer::iterator it = stream.itWrite + sizeof(T);
assert(it <= stream.vBuffer.end());
*(T*)&*stream.itWrite = lhs;
stream.itWrite=it;
return stream;
}
template<typename T>
inline StreamBuffer& operator<<(StreamBuffer& stream, const T* lhs)
{
//Do not serialize raw pointers, use MKH::Memory::SerializingPointer

char CompileTimeAssertion[0];
return stream;
}


Magmai Kai Holmlor

"Oh, like you''ve never written buggy code" - Lee

[Look for information | GDNet Start Here | GDNet Search Tool | GDNet FAQ | MSDN RTF[L] | SGI STL Docs | STFW | Asking Smart Questions ]

[Free C++ Libraries | Boost | ACE | Loki | MTL | Blitz++ ]

Shamelessly ripped from Oluseyi

Share this post


Link to post
Share on other sites
you know its funny, after reading most of this (especially the first reply by saberman) it makes me wonder about some things. first off, the struct presented does NOT show any form of destructor hence the dynamically created array cant be destroyed automagically by the compiler once the varible goes out of scope. so a default copy should work fine unless STL is borken in some way. not trying to flame or anything, but doesnt the default ctor and dtor suppsed to do nothing (except deal with filling the vtable if needed)? also i never had something that i newed to a local varible (like he is doing) which the pointer was then copied to a global storage area (much like what sdt::list must be doing) have the object i created automagically get deleted when the tmp pointer went out of scope. its just an observation i am making. what is the correct result for a compiler for the following code?


  
char *globalPtr=NULL
{
char *tmpPtr = new char[10];
globalPtr = tmpPtr;
}
// is globalPtr valid still?

// if is then his original code should work




Share this post


Link to post
Share on other sites

  • Advertisement