Arrays of dynamic objects and operator[]

A

Andrew.Morgan

Hi

I have three classes TDigest, THash and TSHA1.

//---------------------------------------------------------------------------
class TDigest
{
public:
// Constructors.
TDigest(int Count);
TDigest(const TDigest &Source);

// Destructors.
~TDigest();

// Operators.
TDigest& operator=(const TDigest &Source);
unsigned char& operator[](const int Index);

// Functions.
int Count();

private:
int count;
unsigned char *v,illegal;
};

TDigest has a variable length dynamic array, and is the return type of
THash::Digest() and its descendants. The operator[] provides
access to the individual bytes that make up the hash.

//---------------------------------------------------------------------------
class THash
{
public:
// Constructors.
THash();
THash(const THash &Source);

// Destructors.
virtual ~THash();

// Operators.
THash& operator=(const THash &Source);

// Functions.
virtual void Init(unsigned int Count);
virtual void Block(char* Block);
virtual TDigest Digest();

protected:
unsigned int count;
virtual void Process();
};

THash is the base class for implementing a family of hash algorithms.

//---------------------------------------------------------------------------
class TSHA1:public THash
{
public:
// Constructors.
TSHA1();
TSHA1(const TSHA1 &Source);

// Destructors.
~TSHA1();

// Operators.
TSHA1& operator=(const TSHA1 &Source);

// Functions.
void Init(unsigned int Count);
void Block(char* Block);
TDigest Digest();

protected:
unsigned int blocks,expand[80],work[5],hash[5],round[4];
void Process();
};

TSHA1 produces the SHA-1 hash for the supplied blocks of data.

My dilemma begins when trying to return the digest.

The following works, but has the overhead of temporaries with
associated constructor and destructor calls.

TDigest THash::Digest()
{
TDigest digest(20); // setup 160-bit digest.
int index = 0;
for...
for...
digest[index++] = ...
return digest;
}

What I would prefer to do is:
TDigest* THash::Digest()
{
TDigest digest = new TDigest(20); // setup 160-bit digest.
int index = 0;
for...
for...
digest[index++] = ...
return digest;
}

However, at the digest[index++] point, how does the compiler
distinguish between an array of TDigest objects, or the operator[] to
access a specific byte of the digest's internal array? In this case,
the compiler appears to assume that an array of objects has been
created (which seems odd to me since there was no "new TDigest[count]"
involved) and generates code to access the index-th digest object,
which does not exist beyond 0 (digest is digest[0]).

The following works:
digest[0][index++] = ...
but is non-intuitive. The non-intuitive problem persists since:
....
TDigest *result = sha.Digest();
for(int i = 0; i < result->Count(); i++)
{
... = result[0];
}

Unfortunately, references do not help either, since the compiler
errors:
"Attempting to return a reference to a local variable 'result'"

I had hoped that my Meyers' or Sutter's texts would shed some light on
this, but I am still in the dark.

Thanks in advance.

Andrew
 
U

Unforgiven

My dilemma begins when trying to return the digest.
What I would prefer to do is:
TDigest* THash::Digest()
{
TDigest* digest = new TDigest(20); // setup 160-bit digest.
int index = 0;
for...
for...
digest[index++] = ...
return digest;
}

However, at the digest[index++] point, how does the compiler
distinguish between an array of TDigest objects, or the operator[] to
access a specific byte of the digest's internal array?

What's important here is how the compiler views digest. It is, first and
foremost, a pointer. That it's a pointer to a TDigest is not really
important to the compiler (in this scenario). For a TDigest object, it would
use operator[] as defined on TDigest, but for TDigest*, it uses the
operator[] as defined for pointers, which is memory indexing.

As you noted, one way to get at TDigest.operator[] is digest[0][index++],
but that's not really natural.

What you need to do is dereference the pointer before accessing operator[]:
(*digest)[index++]

It's not really elegant syntax, but it's necessary.
 
A

Andrew Morgan

My dilemma begins when trying to return the digest.
What I would prefer to do is:
TDigest* THash::Digest()
{
TDigest* digest = new TDigest(20); // setup 160-bit digest.
int index = 0;
for...
for...
digest[index++] = ...
return digest;
}

However, at the digest[index++] point, how does the compiler
distinguish between an array of TDigest objects, or the operator[] to
access a specific byte of the digest's internal array?

What's important here is how the compiler views digest. It is, first and
foremost, a pointer. That it's a pointer to a TDigest is not really
important to the compiler (in this scenario). For a TDigest object, it would
use operator[] as defined on TDigest, but for TDigest*, it uses the
operator[] as defined for pointers, which is memory indexing.

As you noted, one way to get at TDigest.operator[] is digest[0][index++],
but that's not really natural.

What you need to do is dereference the pointer before accessing operator[]:
(*digest)[index++]

It's not really elegant syntax, but it's necessary.

Thanks for the prompt reply!

I guess I must just accept the overhead of copy constructor and
destructor and use:

TDigest TSHA1::Digest()
{
TDigest result(20);
int pos = 0;
for...
for...
result[pos++] = ...
return result;
}

I did notice in the version below with dereferencing that the copy
constructor fires (on return), but no destructor!

TDigest& TSHA1::Digest()
{
TDigest *result = new TDigest(20);
int pos = 0;
for...
for...
(*result)[pos++] = ...
return *result;
}
....
TSHA1 sha;
....
TDigest digest = sha.Digest();

digest is copy constructed from *result, but *result is not deleted? A
memory leak?!

This brings one to the core issue:
When a class is the return type, how should it be constructed?

Just a "direct" object?

TDigest THash::Digest()
{
TDigest result(20);
....
return result;
}
 
J

Jonathan Mcdougall

class TDigest
{
public:
...
unsigned char& operator[](const int Index);
...
};
My dilemma begins when trying to return the digest.

The following works, but has the overhead of temporaries with
associated constructor and destructor calls.

TDigest THash::Digest()
{
TDigest digest(20); // setup 160-bit digest.
int index = 0;
for...
for...
digest[index++] = ...
return digest;
}

What I would prefer to do is:
TDigest* THash::Digest()
{
TDigest digest = new TDigest(20); // setup 160-bit digest.

Illegal

TDigest *digest = new TDigest(20);
int index = 0;
for...
for...
digest[index++] = ...
return digest;
}

However, at the digest[index++] point, how does the compiler
distinguish between an array of TDigest objects, or the operator[] to
access a specific byte of the digest's internal array?

'digest' is a TDigest* and what you want is to access
TDigest::eek:perator[](), no ?

(*digest)[index++] = ...


digest => TDigest*
(*digest) => TDigest
(*digest)[](x) => unsigned char&

Don't forget to delete the digest you got somewhere.


Jonathan
 

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

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,564
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top