Solved

delete and void *

Posted on 1998-05-05
38
826 Views
Last Modified: 2010-04-10
I have a situation where rather than use a struct for access to elements within a database, I want to use an array of void pointers.  This will allow me to create generic routines for many of the basic data access routines.  When allocating space for the data, I will access a struct specific to the data file to let me know the size of the field and the type (Eg: char[20], double) allowing me to do something akin to:

void *genericPointer1 = (void *)new char[20];
void *genericPointer2 = (void *)new double;

So far, so good.  The question I have is: Is there a defined result if deleting a void *?  I think the result of

delete genericPointer1;

is undefined and should possibly be

delete (char *)genericPointer1;

Is the latter actually a legal statement?

I think the answer to this will probably be fairly simple, but if it isn't and more points need to be allocated to the question , just let me know.

Thanks in advance

Sean Hannan
0
Comment
Question by:spearhead
  • 19
  • 16
  • 2
  • +1
38 Comments
 
LVL 10

Accepted Solution

by:
RONSLOW earned 150 total points
Comment Utility
answer coming
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
As long as you only point to an object of a primitive types or aggregates (simple structs) you are ok.  If you are using it for arrays or non-simple structs/classes then you are in trouble.

Unfortunately the statement is (AFAIK) legal, but it may not do what you want.  For example, when the void pointer is pointing to an array of chars, the array may not be cleaned up correctly because you are not calling the delete[] operator.

Another idea, if you are only pointing to primitives, might be to use a union (probably with a tag saying what type of data is there) eg.

stuct AnyPrimitiveType {
  enum {INT,BOOL,DOUBLE} type;
  union {
    int ivalue;
    bool bvalue;
    double dvalue;
  };
  AnyPrimitiveType() : type(INT), ivalue(0) {}
};

then you can allocate a AnyPrimitiveType object and keep a pointer to it.

This won't help you with strings though, so a little more better is

struct AnyType {
  enum EType {INT,BOOL,DOUBLE,STRING} type;
  union {
    int ivalue;
    bool bvalue;
    double dvalue;
    char* svalue;
  };
  EType Type() const { return type; }
  void Clear() {
    if (type == STRING) delete svalue;
    type = INT;
    ivalue = 0;
  }
  AnyType() : type(INT), ivalue(0) {}
  AnyType(const AnyType& x) : type(INT), ivalue(0) { *this = x; }
  ~AnyType() { Clear(); }
  AnyType& operator=(const AnyType& x) {
    if (this != &x) {
      Clear();
      type = x.type;
      switch (type) {
      case INT: ivalue = x.ivalue;
      case BOOL: bvalue = x.bvalue;
      case DOUBLE: dvalue = x.dvalue;
      case STRING: svalue = strdup(x.svalue);
      }
    }
    return *this;
  }
  void Set(int i) { Clear(); type = INT; ivalue = i; }
  void Set(bool b) { Clear(); type = BOOL ; ivalue = b; }
  void Set(double d) { Clear(); type = DOUBLE; ivalue = d; }
  void Set(const char* s) { Clear(); type = STRING; svalue = strdup(s); }
};

Another approach is to create a class wrapper for each of the types of data you will use (some may already be classes).  These should all derive from the same base class (with a virtual destructor).  Then you can keep a pointer to the base class which can point to any of the derived classes (eg a derive class that holds an int, or a double, or a string).  And the delete operator (being virtual) will call the approriate destructor.
 
0
 

Author Comment

by:spearhead
Comment Utility
The reason why I have chosen to do it as mentioned is because, within the application framework we are using, we have a table object (spreadsheet-like) to display data item son the screen.  This allows access to each of the individual columns or fields in a given record.  I need to store the whole record somewhere, and not just the items that are to be displayed within the table record object, hence the need to store something similar to a struct.

The problem arises because I see no point in reading a data record from a file and then reading it again to display in the screen.  The second read is required because access is only allowed by field number (int).  This number can then not be applied to a struct or union, however it can be used as an array index.  The fact that I am trying to make these routines generic means that I need to use a void * to allow for different data types in the different fields.

Even if this was all encapsulated in a class, an array subscript is still required in order to access a field specific member variable.
0
 

Author Comment

by:spearhead
Comment Utility
As this method will only be applied to fields within a database record, the issue of delete[] for arrays will not (I believe) arise.  The use of char * should (?) still be catered for by the delete statement below

void *voidPointer = (void *)new char[20]
delete  (char *)voidPointer
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
not really .. if you allocate with new[] you should deallocate with delete[].

You shouldn't use void* as a generic pointer in C++ .. in C you had little choice.. in C++ you do.

You can use templates to generate the classes for you eg. (from an answer I gave to someone else about this..)

class CGeneric {
public:
     virtual int Type() const=0;
     virtual ~CGeneric() {}
     // whatever else is common to all
};

template <class OBJECT, int TYPE> class TGeneric : public CGeneric{
     OBJECT m_object;
public:
     CGeneric(const OBJECT& x) : m_object(x) {}
     operator OBJECT& () { return m_object; }
     operator const OBJECT& () const { return m_object; }
     virtual int Type() const { return TYPE; }
};

enum GENERIC_TYPE {
     DOUBLE_OBJECT,
     INT_OBJECT
};
typedef TGeneric<double,DOUBLE_GENERIC> CDoubleGeneric;
typedef TProperty<int,INT_PROPERTY> CIntProperty;

No you can use a CProperty* pointer to point to an object of any of the types you have defined.  Simply add extra lines to the enum and the typedefs for any types of data that you want.

eg.

CProperty* pPointer;
pPointer = new CDoubleGeneric(1.0);
...
pPointer = new CIntGeneric(1);
...

you can test the type of data three with
if (pPointer->Type() == DOUBLE_OBJECT:
   double dvalue = static_cast<CDoubleGeneric*>(pPointer);

An alternative to using an enum (and the corresponding template param) is to use C++ RTTI to check [ie. check typeid(*pPointer) ]
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
In your case you'd have an array of CGeneric* pointers.

You can read your data in and allocate an approriate CGenericXXXX pointer for each column value and put them into the CGeneric*[] array.

eg.

CGeneric* fields[2];
int field0;
double field1;
// read my record here
fields[0] = new CIntGeneric(field0);
fields[1] = new CDoubleGeneric(field1);
// you can later do
for (int i = 0; i < 2; i++) {
  delete fields[i];
}

You could even add a member to CGeneric and TGeneric like this...
  virtual void CopyToBufferAsString(char* buffer) const;
And specialise it when you define the specific classes eg
  void CIntGeneric::CopyToBufferAsString(char* buffer) {
    sprintf(buffer,"%d",m_object);
  }
  void CDoubleGeneric::CopyToBufferAsString(char* buffer) {
    sprintf(buffer,"%f",m_object);
  }
etc.
Then you can use this to get the string form for display in your 'spreadsheet' for each field



0
 

Author Comment

by:spearhead
Comment Utility
I haven't done anything with templates to data so I have printed your comments so I can review them and sort out and understand templates properly before replying.  I have a feeling I may need to up the points on this one though!
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
That's OK .. templates are very much like #define macros in C.

You are probably familiar with #define macro functions like
  #define MAX(X,Y) ((X)>(Y)?(X):(Y))
this compares with the (inline) function
  inline int MAX (int x, int y) { return x>y ? x : y; }
The macro form has one main advantage over the function version.  It works with any type of data (you can use MAX on an int, or a double, or anything that understands '>')

As C++ supports overloading (same function name with different arg types), you can actually write several MAX functions for different types like this:

  inline int MAX (int x, int y) { return x>y ? x : y; }
  inline double MAX (double x, double y) { return x>y ? x : y; }
  inline char MAX (char x, char y) { return x>y ? x : y; }

Unfortunately, you need to write the same function (but with different type) several times, and if you need a MAX function for another type, you need to do it again.

You can automate this a bit with a #define. eg
  #define generate_MAX(T) inline T MAX(T x, T y) { return x>y ? x : y); }
Then you could say
  generate_MAX(int)
  generate_MAX(double)
etc.  This is a little better, because you only need to write the body once (in the #define) but you still need to explicitly expand the generate_MAX function for each type.

A function template is the next step from this.  It looks very much like the #define above
  template <class T> inline T MAX(T x, T y) { return x>y ? x : y; }
Here, the word 'template' is a bit like the #define, and the bits in the '<...>' is like the arg to the macro.  In this case '<class T>' means that we are writing a template, for any type T, for the function MAX.  Whenever we call the MAX function, C++ will automatically generate the corresponding MAX function depending on the arg types of the call.  We don't need to explicitly instantiate the function for each type we may need (as was required with the #define method above).

This same template idea an also apply to class or struct declarations (as well as functions)
eg.

  template <class T> class Pair {
  public:
    T first;
    T second;
  };

This tells the compiler how to generate a class called Pair for various data types.  The difference here is that we need to specify the data type whenever we use wnat to get a specific Pair class. eg.
  Pair<int>
will generate a Pair class with two int members. Similarly
  Pair<double>
will generate a Pair class with two double members.

Anyway .. I this quick intro to templates helps you a bit.  There is a little more to it than that .. but its a start :-)

0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
That's OK .. templates are very much like #define macros in C.

You are probably familiar with #define macro functions like
  #define MAX(X,Y) ((X)>(Y)?(X):(Y))
this compares with the (inline) function
  inline int MAX (int x, int y) { return x>y ? x : y; }
The macro form has one main advantage over the function version.  It works with any type of data (you can use MAX on an int, or a double, or anything that understands '>')

As C++ supports overloading (same function name with different arg types), you can actually write several MAX functions for different types like this:

  inline int MAX (int x, int y) { return x>y ? x : y; }
  inline double MAX (double x, double y) { return x>y ? x : y; }
  inline char MAX (char x, char y) { return x>y ? x : y; }

Unfortunately, you need to write the same function (but with different type) several times, and if you need a MAX function for another type, you need to do it again.

You can automate this a bit with a #define. eg
  #define generate_MAX(T) inline T MAX(T x, T y) { return x>y ? x : y); }
Then you could say
  generate_MAX(int)
  generate_MAX(double)
etc.  This is a little better, because you only need to write the body once (in the #define) but you still need to explicitly expand the generate_MAX function for each type.

A function template is the next step from this.  It looks very much like the #define above
  template <class T> inline T MAX(T x, T y) { return x>y ? x : y; }
Here, the word 'template' is a bit like the #define, and the bits in the '<...>' is like the arg to the macro.  In this case '<class T>' means that we are writing a template, for any type T, for the function MAX.  Whenever we call the MAX function, C++ will automatically generate the corresponding MAX function depending on the arg types of the call.  We don't need to explicitly instantiate the function for each type we may need (as was required with the #define method above).

This same template idea an also apply to class or struct declarations (as well as functions)
eg.

  template <class T> class Pair {
  public:
    T first;
    T second;
  };

This tells the compiler how to generate a class called Pair for various data types.  The difference here is that we need to specify the data type whenever we use wnat to get a specific Pair class. eg.
  Pair<int>
will generate a Pair class with two int members. Similarly
  Pair<double>
will generate a Pair class with two double members.

Anyway .. I this quick intro to templates helps you a bit.  There is a little more to it than that .. but its a start :-)

0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
Oops .. I'm repeating myself .. how did THAT happen?

0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
Answer coming?  I don't remember answering...

Does this help?  What i do is to create a class of objects (derived from a single base class) that represents the different field types you have in your database.  These field do not store the data directly, they have a pointer to the data and are used to access the data (format it, convert it, read and write it etc).  When you read the record, you have a block of data that stores all the fields.  You go through the record and create field access objects (that's what I call them) for each of the fields in the database.  If the field access objects are all the same size, you can store them in an an array and use the field number to get to the access object.  If they are different sizes (probably are) you can store them in an array of pointers to the base class field access object.  

Note that these field access objects don't allocate space to store the field data.  They use the spac already provided.  They are just a C++ interface to the data.  They provided a bunch of non-field-type specific procedures that are avaialble for all accessors, such as get data length, get display length, format to string, etc.  Then some of the derived classes have additional procedures that are specific to the data type.
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
That's pretty much what I suggested.  Its basically external polymorphism.

You can store that data, or store pointer to external data (either works fine .. I originally put in code that had a pointer to the data, but changed it to embed the actual data (it seemed simpler for this case).

But the main point (sic) is .. don't use void* as a generic pointer in C++ .. there are other ways of doing this that won't break your code.

0
 

Author Comment

by:spearhead
Comment Utility
OK, The template introduction made sense but applying it to your initiaal example lost me a little.  From readin it a few times, I think maybe the terminology you have used is a little screwed up but then again, it is probably me who is wrong and just ahven't wrapped my head around it fully yet.

Where did the TProperty/INT_PROPERTY/CIntProperty come from and where would it be used?  Should this have been TGeneric/INT_OBJECT/CIntGeneric as per the previous line?  What is the CProperty * pointer you mentioned?

In your followup, you also mentioned that with templates, C++ would automatically generate classes from the template, but aren't you doing this explicitly with the two typedef's or have I completely missed the point on this one?

Just a quick sideline here as well.  What is the trailing "const" on the virtual void::CopyToBufferAsString(char *buffer) declaration?

This question has now expanded substantially from the original so I'll upgrade the points for it.  I don't really have a guideline yet for the number of points a question like this is worhth, so I'll throw the floor open to you.  How many should it be worth?  Let me know and I'll up them accordingly.
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
Typo - I had another answer that used the names CProperty and TProperty etc.  For your example I (tried to) change this to CGeneric , TGeneric etc.  Guess I should have copied into a text editor and done a find/replace as I stuffed it up.  Sorry.  Just change all occurences of 'Property' etc to 'Generic'.

For template FUNCTIONS the compiler will generate them as required.  For classes, you have to do it yourself .. the typedef is not necessary .. just makes the code a little more readable.

The trailing const means that this member function does not change the object.  ie CopyToBufferAsString does not change the CGeneric object - it just needs to look at (but not touch) the member data.

I think 10000 points would be sufficient :-) ... I don't know .. 100 maybe.

0
 

Author Comment

by:spearhead
Comment Utility
OK, NOw it's all starting to fall into place.  The TProperty etc.. was the part that was getting my confused.  Another quick question:  The template <class OBJECT, int TYPE> obviously requires an OBJECT and a TYPE parameter.  The OBJECT is stored as a private member of the TGeneric class and TYPE is returned from Type(), but where is TYPE stored?  Should this also be a private member of TGENERIC or should it be passed on and stored as a private member of CGeneric, and make the Type() pure virtual function somply a virtual function?  Is it even possible to pass on parameters to a base class via a template?

0
 

Author Comment

by:spearhead
Comment Utility
What is the benefit of adding the trailing "const" to a function declaration.  I am guessing there is some benefit to it but what it is is not immediately obvious (to me anyway!!)  When and where should this be used?
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
TYPE doen't need to be stored.  Because it is an 'int' template parameter, it is just a number (not a type).  Templates can type type args (indicated by the keyword 'class') or a value (eg an int).

One common use for an int parameter is like this

template <class TYPE, int COUNT> class Array {
  TYPE item[COUNT];
  ...
};

You can use this template class like this...
  Array<double,10> myarray
This will create a class with a member that is an array of 10 doubles.

The parameter with the keyword 'class' is a type, the parameter with the keyword 'int' is a value (in this case a number).  NOTE: you can also have 'char' or 'long' etc instead of 'int' as approriate.

Asking where TYPE is stored is similar to asking in
  #define EQUAL(X,N) ( (X) == (N) )
where is X or N is stored.

NOTE: a template generates separate classes .. they are not related and able to share virtual functions UNLESS they derive from the same class.

In this case, each TGeneric class derives from CGeneric .. so this is where any virtual functions need to be declared.

BTW: You _could_ actually store the value of TYPE somewhere .. but that would really be a waste of storage .. TYPE is a literal constant for each TGeneric class.  It would be like storing the constant number 1 in every member of a class.

0
 

Author Comment

by:spearhead
Comment Utility
The two examples you gave in your previous comment are differ from the intial example in that the COUNT in the template is used immediately and X and N are just copies of parameters and also used immediately so there is no requirement to store them.  In the initial example, the int TYPE is not stored anywhere but is referenced as a return value from the Type() function which obviously has to obtain it's return value from somewhere.  This is where I have gone slightly off the rails.  I am currenlty in the process of implementing the initial tmeplate example so it will all possibly become clearer when implemented (or perhaps worse if I can't get a successful compile out of it!!!)
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
Here is my code again, with some (hopefully) explanatory comment.  I hope this is a little clearer with (most?) of the typos fixed up.

// declare a base class for our generic objects
// this is an abstract base class .. we should not
// and cannot create an object of CGeneric
class CGeneric {
public:
    // the Type virtual function will return an int
    // in the derived classes it will return a constant
    // that indicated the type of object.  We make this
    // pure virtual because a CGeneric does not HAVE a type
    // (only the derived classes will have a type)
    virtual int Type() const=0;
    // we need a virtaul destructor so that we can delete
    // a CGeneric pointer that points to a derived objetc
    virtual ~CGeneric() {}
    // whatever else is common to all
};

// TGeneric is a template which creates a class derived
// from CGeneric above.  It is given the type of object
// to be stored (OBJECT) and an integer constant that is
// the type id for this type class (TYPE)
template <class OBJECT, int TYPE> class TGeneric : public CGeneric{
    // here we store the acutal object
    OBJECT m_object;
public:
    // here we init the member from another object
    CGeneric(const OBJECT& x) : m_object(x) {}
    // we can cast a TGeneric into a reference to the
    // OBJECT it holds. This means we can use a TGeneric
    // (most) places we can use an OBJECT.  Also we have
    // both a const and non-const version
    operator OBJECT& () { return m_object; }
    operator const OBJECT& () const { return m_object; }
    // here we override the Type pure virtual from the
    // base class CGeneric.  This simply returns the consant
    // int TYPE that was specifed for this class
    virtual int Type() const { return TYPE; }
};

// here are the types we will support
enum GENERIC_TYPE {
     DOUBLE_OBJECT,
     INT_OBJECT
};

// We'll define some typedefs for easy of reading
// so we can refer to a CDuobleGeneric instead of a
// TGeneric<double,DOUBLE_OBJECT>.
typedef TGeneric<double,DOUBLE_OBJECT> CDoubleGeneric;
typedef TGeneric<int,INT_OBJECT> CIntGeneric;

0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
Look at what the 'expanded' class for a given TGeneric would be.

For example, consider
    TGeneric<double,DOUBLE_OBJECT>
which expands to be (sortof) like this:

class TGeneric<double,DOUBLE_OBJECT> : public CGeneric {
    double m_object;
public:
    CGeneric(const double& x) : m_object(x) {}
    operator double& () { return m_object; }
    operator const double& () const { return m_object; }
    virtual int Type() const { return DOUBLE_OBJECT; }
};

Does this make it clearer?  It is just like macro substitution.  The parameter TYPE is replaced by the constant value used when the template is instaniated (ie. in this case by the constant DOUBLE_OBJECT).

0
 

Author Comment

by:spearhead
Comment Utility
OK, I think I am on the right track now.  Most of it has been cleared up and I see how the whole thing hangs together now so I should be able to continue on from here.  Sorry for taking so long to pick it up.  I don't know what I was thinking with the TYPE member variable question, but as soon as I got your "expanded CDoubleGeneric" comment I could have just about smashed my head on the table.  I was just substituting the wrong things for TYPE and probably should just have treated it as an "enhanced #define".

I may leave this question open for a couple of days in case I have a follow up if that's OK (??)
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
Yeup .. that's fine

Mind you, accepting doesn't stop comments from being made etc.. but it does mean other experts need to pay to come in an contribute.

Templates are a powerful features .. I hope my explanations have been OK and I have not said anything that will give you a wrong idea about the way they work.  It would be well worth your while reading up on templates in a good C++ book to make sure you understand how they work and that I have not led you astray in my rough and ready explanation.

I think, once you have the basic concept (ie. they are a bit like an enhanced #define) then it becomes much easier.  Its a bit like pointers .. they give many programmers problems when first encountered, but once the idea 'clicks' it all make sense.


0
 

Author Comment

by:spearhead
Comment Utility
I may have spoken a little soon!  I have one problem at the moment. I have set up the code as presented by with one type (??) correction in the template.  I had to change the
CGeneric(const OBJECT & x) : m_object(x) {}
to
TGeneric(const OBJECT & x) : m_object(x) {}
to achieve a successful compile.

The problem appears to be the fact the we need to cast a CGeneric pointer whenever we wish to obtain the value of m_object.
CGeneric *fields[2];
int field0 = 1;
double field1 = 2.0;
fields[0] = new CIntGeneric(field0);
fields[1] = new CDoubleGeneric(field1);

// This doesn't work as fields[0] is a CGeneric *
int test1 = *fields[0];  

// This does work because of the explicit typecast
int test2 = *((CIntGeneric *)fields[0]);

This means that we need to know at compile time or determine via a switch statement on CGenericObject::Type() so that we can explicitly cast to a derived class to obtain the value of m_object?

Is this "theory" correct or am I completely wrong (again)?

I am also chasing up a book on templates to help me out further on this one.
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
Oops again .. I did say "(most?)" typos fixed :-) .. yes, that should have been TGeneric instead of CGeneric for the construtor.

Another (variation) on the solution might help here .. I mentioned before about using C++ RTTI (run-time type info) instead of using the TYPE parameter and Type() parameter.

class CGeneric {
public:
    virtual ~CGeneric() {}
    // whatever else is common to all
};

template <class OBJECT> class TGeneric : public CGeneric{
    OBJECT m_object;
public:
    TGeneric(const OBJECT& x) : m_object(x) {}
    operator OBJECT& () { return m_object; }
    operator const OBJECT& () const { return m_object; }
};

now we just say..

CGeneric* field[N];
...
TGeneric<int>* pInt = dynamic_downcast<TGeneric<int>*>(fields[0]);
if (pInt) {
  int test = *pInt;
  ...
}

The dynamic_downcast operator returns NULL if fields[0] does not point to a TGeneric<int> (but instead points to some other TGeneric<xxx> class).

This way, you don't need switch based on types, and you don't need that enum of type constants, nor a Type() virual functions.  You just need to dynamic_cast to the required type and check for a NULL pointer.  Overall this is a little simpler.

You can still check for the type by using the 'typeid' function. eg

if (0 == stricmp(typeid(*fields[0]).name,"TGeneric<int>")) {
  ...
}

I think I'vegotten the syntax ok there.

0
 

Author Comment

by:spearhead
Comment Utility
Even with this variation, there is a problem.  As far as I can tell, we still need to implement a void * there somewhere.  With the RTTI example presented above, all variables are limited to within the if statement where they are declared and cannot be outside that.  
With a void *, we are to able initially declare it and assign any data to it that we want.  We still need to cast it somewhere to the require type obviously in order to make it's contents meaningful.  Basically, htis puts us right back at square one with the void *.  The only advantage that we have now is that our class destructor will clear up the memory based on OBJECT rather than attempting to delete a void *.  If we use a void *, we can declare a function in CGeneric so that the value of m_object is returned via the void *.  This void * can then be passed into any other function accepting a void * and type cast accordingly.

*******

With a void *:

// Forward declaration of external function.
FieldOperationFunction(value, fields[0]->Type());

// Sample GetValue() .. or similar
void *CGeneric::GetValue() { return (void *)m_object; }

CGeneric *fields[2];
int field0 = 1;
double field1 = 2.0;
fields[0] = new CIntGeneric(field0);
fields[1] = new CDoubleGeneric(field1);

// Using a void *
void *value = fields[0]->GetValue();
FieldOperationFunction(value, fields[0]->Type());

*******

Not using a void *

// Forward declaration of external function.
// We can't use a template for this as below because FieldOperationFunction() will
// be performing type specific operations.
// template <class T> FieldOperationFunction (T value);
// So we need to overload:
FieldOperationFunction(int value) {int specific functions };
FieldOperationFunction(double value) { double specific functions };

// This required in case of int
TGeneric<int>* pInt = dynamic_downcast<TGeneric<int>*>(fields[0]);
if (pInt)
{
    int Value = pInt;
    FieldOperationFunction(value);
}

// This required in case of double
TGeneric<double>* pDouble = dynamic_downcast<TGeneric<double>*>(fields[0]);
if (pDouble)
{
    int value = pDouble;
    FieldOperationFunction(value);
}
...

This is most certainly a lot harder to maintain given the number of overloaded functions that will need to be povided.

I hope all that makes some sense.
0
 

Author Comment

by:spearhead
Comment Utility
Ooops....I think my statement that a function template couldn't be used was completely wrong!  We can use a template for FieldOperationFunction providing we still have separate function bodies for each different type to be used.  (I think!)
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
>As far as I can tell, we still need to implement a void *
>there somewhere.

No .. you don't need any void* at all.  Just a CGeneric*

>With the RTTI example presented above, all variables are
>limited to within the if statement where they are declared
>and cannot be outside that.

Eh?  Variables can be declared whereve you want.

FieldOperationFunction should be made a virtual member of CGeneric and then overridden for each TGeneric<xxx>.

So the code is even easier

fields[0]->FieldOperationFunction();
fields[1]->FieldOperationFunction();

the actual implementation of FieldOperationFunction would depend on what TGeneric<xxx> object eahc fields[x] was pointing to.  virtal functions do all the work for you.

0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
I took a couple of hours off and now there is too much to read!  I'll leave it to Roger to finish things up, but I wanted to point out one thing.  dynamic_downcast<> should be just dynamic_cast<>.  The "dynamic_downcast" that Roger is thinking of is a (MFC?) macro and must be in uppercase and has a different syntax.
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
oops again ..

DYNAMIC_DOWNCAST() is an MFC macro (based on MFC's own run-time type checking built into CObject).  One day maybe it may be rewritten to use dynamic_cast instead.

dynamic_cast<>() is a built int C++ operator.  Note that where possible you should use these new casting operators (const_cast etc) to make casts easier to find in the code and make their purpose more obvious to both the programmer and the compiler.

0
 

Author Comment

by:spearhead
Comment Utility
OK...Just a clarification of my previous comment.

The part about variables being local to an if statement is like this

// This required in case of int
TGeneric<int>* pDouble = dynamic_downcast<TGeneric<int>*>(fields[0]);
if (pInt)
{
    // value is local to this if statement
    int Value = pInt;
    FieldOperationFunction(value);
    ...
}

// This required in case of double
TGeneric<double>* pDouble = dynamic_downcast<TGeneric<double>*>(fields[0]);
if (pDouble)
{
    // value is local to this if statement
    double value = pDouble;
    FieldOperationFunction(value);
    ...
}

// ***
// value cannot be used here as it is now out of scope. Wouldn't this lead to a rather long
// series of if statemens if a number of operations had to be performed on value.
// ***

If a void * were used, we could simply have

void *value = fields[0]->GetValue()
FieldOperationFunction(value, type);

If we had a number of function calls / operations instead of just FieldOperationFunction(), they would all have to be called inside the if loop for each and every type that we are using our TGeneric class with because of the limited scope of value.

Does that make any more sense?
0
 

Author Comment

by:spearhead
Comment Utility
Any function taking an OBJECT parameter or returning one cannot be made virtual because OBJECT (and TYPE for that matter) has no meaning at CGeneric base class level.
0
 

Author Comment

by:spearhead
Comment Utility
Damn - another stupid comment - forget that last one about the virtual function....This really is going to be a long day at the office I just know it!!!
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
FieldOperationFunction should take a CGeneric* pointer arg, then it can do the approriate things depending on the actual type of field.

The void* just won't help here .. you do the same thing with CGeneric*.

0
 

Author Comment

by:spearhead
Comment Utility
OK...I am having to make some code changes to work in the teplates and thiunk I should be able to work them in OK now that I have some idea of what I am doing.   One other thing though.  I have changed delete to delete[] for char * as follows.

char *test = new char[length];
// old delete
// delete test;
// new delete
delete test[];

but the trailing ] in the delete generates a syntax error in Visual C++ 5.  Why is this?

0
 
LVL 3

Expert Comment

by:stefanr
Comment Utility
It's not
delete test[];
it's
delete[] test;

delete and delete[] are different operators.
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
yeah .. what he said !!!

if you allocate with
    MyObject* pObjectArray = new MyObject[n];
then you MUST free with
    delete[] pObjectArray.

If you allocate with
    MyObject* pObject = new MyObject;
then you MUST free with
    delete pObject

Don't mix 'new xxx[n]' with plain 'delete' or plain 'new' with 'delete[]' -- this will cause problems !!

0
 

Author Comment

by:spearhead
Comment Utility
Ooops - my fault!  I'll leave this question open a little while while I work on the templates bit but so far it is coming along OK so I will answer and finaly put an end to this question (or should that be saga) in a couple of days time if that's alright.
0
 
LVL 10

Expert Comment

by:RONSLOW
Comment Utility
OK -- don't leave it too long or EE will auto-grade the answer (with a C) and close it on you.

0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now