Arrays of derived objects

D

Daniel T.

"Jack said:
Thank you Daniel, that sounds like a good idea. If I provide only
constructors that take a destructor function as a parameter, the user must
provide one in order to use my Buffer class. Do you think the following code
would be correct? Is it portable? Does memory alignment work in it?

using namespace std;
template<class T>
class Buffer
{
public:
Buffer(T* p, size_t ElemSize size_t ArraySize, void(*pfn)(T*)) :
_p(ArraySize), _i(ElemSize), _pfn(pfn)
{
void* u=reinterpret_cast<void*>(p);
for(size_t j=0;j<ArraySize;++j)
{
_p[j]=reinterpret_cast<T*>(u);
u+=ElemSize;
}
}
~Buffer() { (*pfn)(&_p[0]); }
T& operator[](size_t i) { return *_p; }
const T& operator[](size_t i) const { return *_p; }

private:
vector<T*> _p;
size_t _i;
void(*_pfn)();
};

Regards,

Jack


Please try to compile your code before posting...

But you are on to something. Here is your code corrected. What makes it
work is the templated constructor...

template < class Base >
class Buffer
{
Buffer( const Buffer& );
Buffer& operator=( const Buffer& );
public:
template < typename Derived >
Buffer( Derived* p, size_t ArraySize, void(*pfn)(void*) )
: _memory( p )
, _p( ArraySize )
, _pfn( pfn )
{
for ( int i = 0; i < ArraySize; ++i )
_p = &p;
}
~Buffer() { (*_pfn)(_memory); }
Base& operator[](size_t i) { return *_p; }
const Base& operator[](size_t i) const { return *_p; }

private:
void* _memory;
vector<Base*> _p;
void(*_pfn)(void*);
};

void kill( void* p ) { }
void kill2( void* p ) {
B* b = reinterpret_cast<B*>( p );
delete [] b;
}

int main() {
B b[20];
Buffer<A> buf( &b[0], 20, &kill );
assert( &buf[0] == &b[0] );
assert( &buf[1] == &b[1] );

B* b2 = new B[5];
Buffer<A> buf2( b2, 5, &kill2 );
assert( &buf2[0] == &b2[0] );
assert( &buf2[1] == &b2[1] );

}

Your loosing some type safety though, and the Buffer has to maintain an
extra pointer per object over a more conventional approach.

Also, copying Buffers would be problematic, which is why I disable it.
 
J

Jack

Daniel T. said:
Please try to compile your code before posting...

Yes, I am sorry about the syntax errors in my code. I don't have a compiler
in my mobile computer and I did not notice the bugs until I had already sent
the message.
But you are on to something. Here is your code corrected. What makes it
work is the templated constructor...

Yes, that's it. A suitable combination of templates and void*s does the
trick. In the constructor another template parameter is allowed because it
does not change the object type, so the object can still be passed into
functions in the library.

Thank you very much for this solution.

Jack
template < class Base >
class Buffer
{
Buffer( const Buffer& );
Buffer& operator=( const Buffer& );
public:
template < typename Derived >
Buffer( Derived* p, size_t ArraySize, void(*pfn)(void*) )
: _memory( p )
, _p( ArraySize )
, _pfn( pfn )
{
for ( int i = 0; i < ArraySize; ++i )
_p = &p;
}
~Buffer() { (*_pfn)(_memory); }
Base& operator[](size_t i) { return *_p; }
const Base& operator[](size_t i) const { return *_p; }

private:
void* _memory;
vector<Base*> _p;
void(*_pfn)(void*);
};

void kill( void* p ) { }
void kill2( void* p ) {
B* b = reinterpret_cast<B*>( p );
delete [] b;
}

int main() {
B b[20];
Buffer<A> buf( &b[0], 20, &kill );
assert( &buf[0] == &b[0] );
assert( &buf[1] == &b[1] );

B* b2 = new B[5];
Buffer<A> buf2( b2, 5, &kill2 );
assert( &buf2[0] == &b2[0] );
assert( &buf2[1] == &b2[1] );

}

Your loosing some type safety though, and the Buffer has to maintain an
extra pointer per object over a more conventional approach.

Also, copying Buffers would be problematic, which is why I disable it.
 
J

Jack

Hi again,

I began wondering whether it might be possible to code something like this:

template<class Derived>
void kill(void* p)
{
delete[] reinterpret_cast<Derived*>(p);
}

and then instantiate this template function and substitute its address into
the _pfn member of the Buffer class in the templated constructor when the
Derived type is known.

However, at least in MSVC++ 6 I didn't make it compile. Any ideas?

Regards,
Jack
 
D

Daniel T.

"Jack" <[email protected]> said:
Hi again,

I began wondering whether it might be possible to code something like this:

template<class Derived>
void kill(void* p)
{
delete[] reinterpret_cast<Derived*>(p);
}

and then instantiate this template function and substitute its address into
the _pfn member of the Buffer class in the templated constructor when the
Derived type is known.

However, at least in MSVC++ 6 I didn't make it compile. Any ideas?

As I showed in my sample code, your buffer class can't guarantee that
the array passed in was created with new[] so your Buffer can't assume
that delete[] is appropriate.

However, you can use the above kill function in the Buffer class I
already provided...

Buffer buf( new B[5], 5, &kill<B> );
 
J

Jack

Daniel T. said:
As I showed in my sample code, your buffer class can't guarantee that
the array passed in was created with new[] so your Buffer can't assume
that delete[] is appropriate.

I can document that or provide another way to force the correct deallocation
function be used (for example, have different Buffer constructors for each
case or something). Anyway I would like to know whether it is possible to
have that functionality inside the Buffer class itself. By the way, do you
think it would be a good idea to replace the void*s by Base*s in the Buffer
class? Would it be better type safety or just misleading?

Jack
 
D

Daniel T.

As I showed in my sample code, your buffer class can't guarantee that
the array passed in was created with new[] so your Buffer can't assume
that delete[] is appropriate.

I can document that or provide another way to force the correct deallocation
function be used (for example, have different Buffer constructors for each
case or something). Anyway I would like to know whether it is possible to
have that functionality inside the Buffer class itself. [/QUOTE]

Sure, just make the pointer assignment in the constructor body instead
of passing it in as a parameter.

By the way, do you
think it would be a good idea to replace the void*s by Base*s in the Buffer
class? Would it be better type safety or just misleading?

That would be a bad idea because its undefined behavior.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,577
Members
45,052
Latest member
LucyCarper

Latest Threads

Top