Manual construction and destruction

Started by
35 comments, last by Servant of the Lord 11 years, 5 months ago
Because it isn’t necessarily defined.


[EDIT]
Actually I tested this out about 5 years ago when I was not so advanced. I think I either made a mistake in my testing or I misinterpreted another error as being caused by copy-constructor.

Today, I see no reason it should not work. I am going to redo my tests.
[/EDIT]


[EDIT2]
It does work. I don’t know what my problem was in the past.
When possible, copy-constructors should be used as they reduce construction to a single stage instead of construct-then-copy (though poor implementations of copy constructors may yield the exact same result).

The new code inside of Allocate() is this:

// Construct and copy all the items in the newly allocated array.
for ( LSUINT32 I = Parent::m_tLen; I--; ) {
// Construct new.
new( &ptNew ) _tType( Parent::m_ptData );
// Destroy old.
Parent::Destroy( static_cast<_tDataType>(I) );
}

You could also use copy-constructor inside Insert() for the last element but you will have to rewrite the following array. I leave that up to you.
[/EDIT2]


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Advertisement
Note that L. Spiro's code is not exception safe, so if you use exceptions in your code, you may not want to copy it directly.
I mentioned that in the post but it was a bit hidden under a small wall of text.
I have underlined it for clarity.



Why unsigned char* over char*?

When working with strings, I use [color=#0000ff]char. With data, [color=#0000ff]unsigned char. The reason is for safety while using the >> operator.
With [color=#0000ff]char, it will carry the sign bit, which is rarely desirable when working with raw binary data.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Man, the collective wisdom of a community like this really is a great resource - I'd hate to be trying to learn C++ pre-internet days. I really appreciate the time you have all taken to respond to my questions - especially on nitty-gritty C++ details like this, where I could easily burn myself and only realize it months later. Thank you.


[quote name='Servant of the Lord' timestamp='1351740412' post='4996060']
Why unsigned char* over char*?

When working with strings, I use [color=#0000ff]char. With data, [color=#0000ff]unsigned char. The reason is for safety while using the >> operator.
With [color=#0000ff]char, it will carry the sign bit, which is rarely desirable when working with raw binary data.[/quote]
What effect would that have? Would >> only shift 7 bits and ignore the sign bit?

Nothing in the new array is constructed yet so the next loop constructs the objects and copies from the old list to the new list via the = copy operator. Again one-by-one.[/quote]
Would it be better to use std::move() and the move constructor (since the compiler I use has access to it)?


[quote name='Servant of the Lord' timestamp='1351740412' post='4996060']
How am I supposed to free the raw memory? Just delete[]?

If you allocate with ::operator new(), you have to deallocate with ::operator delete().
[/quote]
How's that work? ::operator new() is saying "use the 'operator new' in global scope". Do classes have implicit operator new()s defined, and the implicit MyClass::operator new() then calls the class constructor? And the implicitly defined MyClass::operator delete() calls the class destructor?

I get that new() constructs the class, but what scope is 'new' in, if ::operator new() does not construct the class?

[hr]
I think it's coming together in my head. When I finish writing the code tomorrow, I'll post it and would like a few eyes to look over it if possible to correct any misunderstandings I might have.

What effect would that have? Would >> only shift 7 bits and ignore the sign bit?
There's two kinds of right shift -- arithmetic and logical. The former fills in the gap created at the top by "sign extending" (basically copying the top bit), and the later fills the gap with 0's.
IIRC, in C++ it's implementation-defined as to which kind of shift will be performed -- someone correct me if I'm wrong, or if C++11 has defined this behaviour!
In my experience every compiler has chosen the same implementation-defined behaviours: shifting a signed integer has always been an arithmetic (sign extending) shift, while shifting an unsigned integer is a logical shift.
You are correct about the standard. Not defined, but so common I have never seen it done any other way on any compiler.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

I'm not at all clear how what you want is any different to a vector at all. Could you elaborate a little on any difference?

For example: I have written a container somewhat like a vector before, except that the memory is not contiguous, push_back is O(1) instead of amortised O(1), and random indexing is instead amortised O(1). There is no internal copy when growing, and each growth allocates an additional block twice the previous size. Searching starts from the largest block and proceeds towards the smaller blocks. The container provides no erase or insert methods. The iterator invalidation rules are relaxed compared to a vector.

You also said that you find yourself needing it, so I'm interested to hear what it is that you need which a vector does not provide. I'm half just wondering, half thinking that you might be mistaken in thinking that a vector isn't suitable.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
I said that I find myself needing it, "more out of convenience and code cleanliness than necessity", and I was very careful to phrase it that way. smile.png

For convenience, because I have multiple classes needing it, I'm making a container that is very much like a vector.
It's a resizable 2D array... that resizes in all four directions (north, east, south, west).

I could just use a 1D vector, and index into it like ((y * width) + x) like I normally do and have done in the past.
Or I could use std::vector< std::vector<Type> > (which I don't particularly like, preferring 1D vectors treated as 2D to ensure the rows all stay the same lengths).

However, using a regular vector requires me to worry about re-arranging the location of the elements anyway.
Example: 3 x 3 grid
0 1 2
3 4 5
6 7 8

Resized to 5 x 5 grid:
0 1 2 3 4
5 6 7 8 +
+ + + + +
+ + + + +
+ + + + +

The elements are no longer in their correct positions, relative to each other.

I'd prefer it to be resized as:
0 1 2 + +
3 4 5 + +
6 7 8 + +
+ + + + +
+ + + + +

Unless I need to extend the grid on the other sides (which I do need in my code):
+ 0 1 2 +
+ 3 4 5 +
+ 6 7 8 +
+ + + + +
+ + + + +

So I created a container class called Grid, which is "a resizable 2D array container, that resizes without harming the relative location of the elements held by the container, and supports negative indices, like a 2D Cartesian grid." (important characteristics underlined - only the first is covered by std::vector)

Yes, I could use std::vector to solve the same thing, but then I'd have to A) have the classes using the vector fumbling around with keeping elements in-sync, B) always be offsetting their indices to correctly support negative indexing. The Grid class handles it all cleanly and intuitively, so I don't bloat the classes using Grid. The Grid class itself is less than 350 lines of code - including excessive whitespacing and comments - so Grid itself isn't bloated or untidy (I'm a real sucker for clean code wink.png).

So yes, "more out of convenience and code cleanliness than necessity".

I'll post it tomorrow, and you can judge it for yourself - I just want to fix one mistake I'm making somewhere in it where resizing is invalidating the data.

Really, the class only took me a few hours to put together - it's just the manual constructing/destructing parts that is new territory for me. And though I could've just made the class wrap std::vector internally (which would still require the class to be made anyway), it's worth taking the extra time to explore the new territory.

When working with strings, I use char. With data, unsigned char. The reason is for safety while using the >> operator.
With char, it will carry the sign bit, which is rarely desirable when working with raw binary data.

Minor correction: with [font=courier new,courier,monospace]char[/font] it may or may not carry the sign bit, depending on the implementation. With [font=courier new,courier,monospace]signed char[/font] or [font=courier new,courier,monospace]unsigned char[/font] you know what will happen.

Also, the effect of [font=courier new,courier,monospace]std::operator>>(std::ostream&, char*)[/font] is different from [font=courier new,courier,monospace]std::operator>>(ostream& unsigned char*)[/font].

Stephen M. Webb
Professional Free Software Developer


There's two kinds of right shift -- arithmetic and logical. The former fills in the gap created at the top by "sign extending" (basically copying the top bit), and the later fills the gap with 0's.
IIRC, in C++ it's implementation-defined as to which kind of shift will be performed -- someone correct me if I'm wrong, or if C++11 has defined this behaviour!

C++ defines shift on a unsigned integer type to be logical shift, however, the standard leaves room for shifting a signed integer to be different from either a logical or arithmetic shift. It would be standard compliant under C++11 for a shift, either left or right, on a signed integer to be a rotation/circular shift rather than either a logical or arithmetic shift. Actually, under C++11 a left shift on a negative signed integer is undefined behavior rather than implementation defined behavior. (Don't ask me to explain this one. To be pedantic a left shift on a signed integer with some non-negative values is also undefined behavior as well, though there's a proposed change to the standard sitting in the committee to fix that oddity.)

This topic is closed to new replies.

Advertisement