Re: Slightly OT: Compilation question
Paul Hsieh wrote:
On Jun 16, 3:16 pm, "Alf P. Steinbach" <al...@start.no> wrote:
[thread cross-posted to comp.lang.c++ and comp.lang.c]
* Paul Hsieh
On Jun 16, 12:10 pm, Ian Collins <ian-n...@hotmail.com> wrote:
Paul Hsieh wrote:
Using new and delete invoke
constructors which you might not want to happen.
No, as class designer you have full control over that. Or from a class
user perpective, if the class in question has at least one user defined
constructor, you usually really want some constructor invoked, so that
you have some invariants established. What C++ does here is to automate
and check what you'd do manually in C, and automation is great. :-)
Its also enforced and the timing has to be at or *before* the time of
first instantiation, unless you do run-time deference tricks (which
have a performance and design impact.)
Furthermore, its
easy to show that STL's vector templates have either ridiculously bad
performance in comparison to hand managed realloc()'s precisely
because of the RAII overhead or else compromise your design to the
point that you might as well use realloc().
Care to demonstrate?
Sure. Lets make a class of mail messages. Note that its impossible
to have an empty mail message (because there is always at least a
header), hence a mail message can only be initialized based on some
input text stream or string; there is no well defined concept of a
default mail message constructor. Further it makes very little sense
to mutate a mail message by changing its contents after the fact. So
its a well motivated read-only class without an empty or default
constructor.
Sure.
Now lets say you want to have a dynamic vector of mail messages (this
is exactly what you would expect a deserialized mailbox essentially to
be). The implementation of STL vectors require that the class have a
default constructor if the vector is modified (which it would be as a
result of incrementally reading the mailbox).
Direct use of a vector would probably be inappropriate, but accepting
that for the sake of argument.
Then, sorry, the information you have is incorrect: std::vector has no
requirement of a default constructor for the element type.
The C++98 requirement of a standard container element class is that it is
assignable[1] and copy constructible.
Even for a mutatible vector?
No not even for those.
I am pretty sure MSVC and WATCOM C/C++
both have problems with this and for good reason. You definitely do
not want to implement a vector as a linked list.
No need for that. You seem to think that reallocation of the vectors
contents requires default construction of some elements (probably because
you think that vector uses new internally). This is not the case. When a
vector resizes, it allocates new memory through an allocator; by the
default, that is std::allocator<T>. The allocator itself obtains raw memory
(the default allocator uses operator new for that, which is not the same as
new; in particular, operator new does not construct anything). Then the
vector copy-constructs the given elements into that raw memory. Finally, it
destroys the elements at the previous location.
I
There are numerous work arounds to this such as creating a wrapper
class which does have an empty constructor which hides a pointer to a
mail message class that starts out NULL.
Hm, now you're talking about a vector of pointers. That imposes no
requirements on the class pointed to. Pointers are already assignable and
copy constructible.
That's why I called it a work around.
But individual new()s to
each one is still going to take extra overhead (performance + memory)
so you would prefer to point into a memory pool of your own which you
maintain with malloc() or realloc() anyways,
It seems you're now talking about a free list or more general custom
allocator, and mixing the requirements of the memory allocator
abstraction level with the requirements of the C++ class type object
abstraction level.
Uhh ... I was just hoping that C++'s std::vector did some "magic" that
made it as fast as I can do with late initialization and realloc()
without requiring I essentially perform work equal or worse than doing
it the C way in the first place.
It turns out, of course, that MSVC and WATCOM C/C++ do no such
thing. When it does a resize it allocates new space, and
simultaneously instantiating extra entries to mitigate constant
resizing thrashing, then copies the old contents into the early part
of its buffer. The first part of this appears to instantiate the
default constructor for your objects before it does the eventual copy
that you want performed.
That would not be standard compliant. The following compiles and executes
fine with g++ (as required by the standard). Please test it on your
compilers:
class X {
// private, unimplemented default constructor
// ==========================================
X ( void );
int x;
public:
X ( int i )
: x ( i )
{}
X ( X const & other )
: x ( other.x )
{}
X & operator= ( X const & rhs ) {
x = rhs.x;
return ( *this );
}
int get ( void ) const {
return ( x );
}
};
#include <vector>
int main ( void ) {
std::vector<X> iv;
for ( unsigned int i = 0; i < 1000; ++i ) {
iv.push_back( X(i) );
}
}
That confusion is unfortunately easy to be led into when programming in
either C or C++, because C does not (let you) properly restrict you, and
C++ accepts almost all of C as a subset, modulo some teeny tiny small
differences.
Well in this case, I don't understand what you are saying. I was
*TRYING* to let C++ solve my problems for me. It didn't and I had to
learn some really dirty grungy details of STL implementations just to
know why. Abstractions only really save you when they work.
I really wonder what happened. Maybe, you should switch to an implementation
of the STL that actually implements the standard.
So when one's first instinct is to Do It Myself then the toolset the
language presents to you overwhelmingly consists of the Wrong Tools, such
as exploiting class level information at the allocator level. Choosing
the Right Tools from that multitude of apparently plausible tools, is
difficult, especially, I think, when one has been misinformed. At the
allocator level you should only be dealing with untyped raw chunks of
memory.
With proper use of C++ you do the custom allocation stuff by defining,
for the class, a custom allocation function (unfortunately called
'operator new') and a ditto custom deallocation function, which deal with
untyped raw storage.
And if you want your "allocation" to be a side effect of a vector
block allocation?
Then, you can use a custom allocator instead of the default
std::allocator<T>. There are pooling allocators and other tricks to
increase performance.
The point is that I don't get to use STL's
vectors. I get to make up my own vector class if I have no default
constructor and I want the vector to be growable.
[snip]
See above: I cannot reproduce your problem. Could you paste some code that
illustrates it?
Best
Kai-Uwe Bux