I'd have thought...
AddArrayPtr[4]->Address
... but I tried it and it doesn't work. Also tried a few other things too without any luck. So I'm sorry to say I don't know how to do it.
Well, I finally figured that rotten thing out. It was, as I was beginning to expect, a template syntax misery. Before getting into that though, I finally had my big realization of the use of vectors! After all these years of wondering what I could do with something like that, I finally discovered a very worthwhile use I could make of them. The beginning of my realization was this from an old Microsoft Visual Studio 98 MSDN CD...
5. Sequence Containers
Sequence containers store and retrieve their data in a sequential fashion. There are three
different sequence containers defined in STL: vector, deque and list.
5.1 Vector
#include <vector>
vector<class TYPE, allocator<class TYPE> >
A vector is similar to a normal C array, except that it can change size automatically as
needed. Data access or modification is random and can be accomplished via operator[].
Insertion or erasure is efficient at the end only. Insertion or erasure of data at the
beginning or in the middle requires shifting the entire array. Therefore, insertion or
erasure anywhere but at the end is a linear operation, meaning that the time to execute the
operation is a function of the number of elements that must be shifted right or left.
Insertion or erasure at the end is a constant operation, meaning that the time to execute
the operation will remain unchanged regardless of how many (or how few) elements are in the
array.
The following function declares a vector of 10 ints, fills the vector with the values
0 - 9, and then prints the values.
typedef vector<int, allocator<int> > INTVECT;
void somefunct()
{
// declare an INTVECT with slots for 10 ints
INTVECT myVector(10);
int i;
for(i = 0; i < 10; i++) // fill myVector with the values 0 - 9
myVector = i;
for(i = 0; i < 10; i++) // print the values in myVector
cout << myVector << ", ";
cout << endl;
}
Output of somefunct:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
The critical part of the above for me was that insertion at the end is a constant operation which won't increase as the length increases, but that insertion anywhere else requires all the contents to shift. Backing up a bit, two days ago I was struggling with some of the underlying concepts and issues relating to my CArray Class where I had added Redim Preserve capability to it about like PowerBASIC's Redim Preserve. There are performance problems involved in that though if used in the context of a 'growable container'. Let me give an interesting example of that.
In one of my work applications, many years ago I had to develop some string routines for converting a number in numeric format such as an int or long to its string representation, and by that I don't mean Str$(1234) but rather ...
"One thousand, two hundred and thirty four"
Here is some code to do that in terms of my String Class...
#define TCLib
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <windows.h>
#include "stdio.h"
#include "tchar.h"
#include "Strings.h"
#include "AlphaNumbers.h"
String strOneDigit(const size_t iNumber)
{
switch(iNumber)
{
case 0:
return _T("zero");
case 1:
return _T("one");
case 2:
return _T("two");
case 3:
return _T("three");
case 4:
return _T("four");
case 5:
return _T("five");
case 6:
return _T("six");
case 7:
return _T("seven");
case 8:
return _T("eight");
case 9:
return _T("nine");
default:
return _T("unknown");
}
}
String strTwoDigit(const size_t iNumber)
{
String strNumber,strTens,strUnits;
size_t iTens=0,iUnits=0;
if(iNumber<10)
return strOneDigit(iNumber);
strNumber=iNumber;
strTens=strNumber.Left(1);
strUnits=strNumber.Right(1);
iTens=strTens.iVal();
iUnits=strUnits.iVal();
if(iTens==1)
{
switch(iUnits)
{
case 0:
return _T("ten");
case 1:
return _T("eleven");
case 2:
return _T("twelve");
case 3:
return _T("thirteen");
case 4:
return _T("fourteen");
case 5:
return _T("fifteen");
case 6:
return _T("sixteen");
case 7:
return _T("seventeen");
case 8:
return _T("eighteen");
case 9:
return _T("nineteen");
}
}
else
{
switch(iTens)
{
case 2:
strNumber=_T("twenty ");
break;
case 3:
strNumber=_T("thirty ");
break;
case 4:
strNumber=_T("fourty ");
break;
case 5:
strNumber=_T("fifty ");
break;
case 6:
strNumber=_T("sixty ");
break;
case 7:
strNumber=_T("seventy ");
break;
case 8:
strNumber=_T("eighty ");
break;
case 9:
strNumber=_T("ninety ");
break;
}
}
if(iUnits)
{
String strLastDigit=strOneDigit(iUnits);
strNumber=strNumber+strLastDigit;
}
return strNumber;
}
String strThreeDigit(const size_t iNumber)
{
String strLeftOne,strRightTwo,strNumber;
strNumber=iNumber;
strLeftOne=strNumber.Left(1);
strRightTwo=strNumber.Right(2);
if(strRightTwo==_T("00"))
{
return strOneDigit(strLeftOne.iVal()) + _T(" hundred");
}
else
{
String strLastTwo=strTwoDigit(strRightTwo.iVal());
return strOneDigit(strLeftOne.iVal()) + _T(" hundred ") + strLastTwo;
}
}
String GetAlphaNumber(const size_t iNumber)
{
if(iNumber<10) // 0 through 9
return strOneDigit(iNumber);
if(iNumber>=10 && iNumber<100) // 10 through 99
return strTwoDigit(iNumber);
if(iNumber>=100 && iNumber<1000) // 100 through 999
return strThreeDigit(iNumber);
else
return _T("");
}
And here is a header for that...
#ifndef AlphaNumbers_h
#define AlphaNumbers_h
String strOneDigit(const size_t iNumber);
String strTwoDigit(const size_t iNumber);
String strThreeDigit(const size_t iNumber);
String GetAlphaNumber(const size_t iNumber);
#endif
So here is a program using the above and my new Redim Preserve capability of my CArray Class to create a 'Growable Container' to accumulate the numbers 0 through 25 converted to Strings....
// Demo37.cpp
// cl Demo37.cpp Strings.cpp AlphaNumbers.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib user32.lib
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#include <windows.h>
#include "stdio.h"
#include "tchar.h"
#include "Strings.h"
#include "CArray.h"
#include "AlphaNumbers.h"
int main()
{
CArray<String> ar; // No initial size or underlying buffer for array ar, i.e., undimensioned
for(size_t i=0; i<=25; i++) // Zero Through Twenty Five
{
ar.Redim(i,true); // Re-Dimension call starting at zero, i.e., will contain 1 element
ar(i)=GetAlphaNumber(i); // For Example, Convert 0 to "zero", 1 to "one", 2 to "two", etc.
ar(i).Print(true); // Output result to console
}
getchar();
return 0;
}
/*
Output:
=========
zero
one
two
three
four
five
six
seven
eight
nine
ten
eleven
twelve
thirteen
fourteen
fifteen
sixteen
seventeen
eighteen
nineteen
twenty
twenty one
twenty two
twenty three
twenty four
twenty five
*/
Note that in PowerBASIC one can dimension an array with a zero subscript like so...
Dim array() As string
Redim array(0)
In the case above array has 1 element accessabe through 0 subscript, so you could write...
array(0)="PowerBASIC : Compile Without Compromise!"
...and the UBound of it would be zero. Well, you could run that concept through a loop starting at zero and doing a Redim Preserve at each iteration to 'grow' the container and maintain its present contents as my C++ code above does with these lines from the loop....
ar.Redim(i,true);
ar(i)=GetAlphaNumber(i);
ar(i).Print(true);
However, that is absolutely not extensible for large data sets or where performance would be required, because for each iteration of the loop a new buffer is going to have to be allocated, the existing string objects moved to that new buffer (and the number is increasing with each iteration), and the old buffer released. In any case though that was a fun program to work on to make sure my new Redim Preserve was working. Here is an update of my CArray Class if anyone would care to run the above code. The changes to it are the Redim Preserve plus it now calculates and uses subscripts like PowerBASIC, i.e., this...
CArray<String> ar(10);
...will now give you eleven elements (0 - 10) instead of the previous ten (0 - 9).
// CArray.h
#ifndef CArray_cpp
#define CArray_cpp
#define NEW new
template <class datatype> class CArray // This entity is a templated class for creating dynamic
{ // multi-dimensional arrays of from one to four dimensions.
public: // It allows for a basic language type syntax for doing
CArray() : pObjs(0) // the above. GNU C++ compilers implement dynamic array
{ // allocation but MS VC++ compilers don't. Since I wanted
this->iNumObjects=0; // to test compile with both I developed this class.
d1=d2=d3=d4=0;
}
CArray(size_t i) : pObjs(0) // One Dimensional Array Constructor
{
d1=i+1, d2=0, d3=0, d4=0;
this->iNumObjects=d1;
this->pObjs = NEW datatype[this->iNumObjects]();
}
CArray(size_t i, size_t j) : pObjs(0) // Two Dimensional Array Constructor
{
d1=i+1, d2=j+1, d3=0, d4=0;
this->iNumObjects=d1*d2;
this->pObjs = NEW datatype[this->iNumObjects]();
}
CArray(size_t i, size_t j, size_t k) : pObjs(0) // Three Dimensional Array Constructor
{
d1=i+1, d2=j+1, d3=k+1, d4=0;
this->iNumObjects=d1*d2*d3;
this->pObjs = NEW datatype[this->iNumObjects]();
}
CArray(size_t i, size_t j, size_t k, size_t l) : pObjs(0) // Four Dimensional Array Constructor
{
d1=i+1, d2=j+1, d3=k+1, d4=l+1;
this->iNumObjects=d1*d2*d3*d4;
this->pObjs = NEW datatype[this->iNumObjects]();
}
bool Dim(size_t i)
{
if(this->pObjs)
delete [] this->pObjs;
d1=i+1, d2=0, d3=0, d4=0;
this->iNumObjects=d1;
this->pObjs=NEW datatype[this->iNumObjects]();
if(this->pObjs)
return true;
else
return false;
}
bool Dim(size_t i, size_t j)
{
if(this->pObjs)
delete [] this->pObjs;
d1=i+1, d2=j+1, d3=0, d4=0;;
this->iNumObjects=d1*d2;
this->pObjs=NEW datatype[this->iNumObjects]();
if(this->pObjs)
return true;
else
return false;
}
bool Dim(size_t i, size_t j, size_t k)
{
printf("Are We Getting In Here???\n");
if(this->pObjs)
delete [] this->pObjs;
d1=i+1, d2=j+1, d3=k+1, d4=0;
this->iNumObjects=d1*d2*d3;
this->pObjs=NEW datatype[this->iNumObjects]();
printf("j = %Iu\n",j);
printf("d2 = %Iu\n",d2);
if(this->pObjs)
return true;
else
return false;
}
bool Dim(size_t i, size_t j, size_t k, size_t l)
{
if(this->pObjs)
delete [] this->pObjs;
d1=i+1, d2=j+1, d3=k+1, d4=l+1;
this->iNumObjects=d1*d2*d3*d4;
this->pObjs=NEW datatype[this->iNumObjects]();
if(this->pObjs)
return true;
else
return false;
}
/*
bool Redim(size_t i, bool blnPreserve)
{
datatype* pObj=NULL;
if(this->pObjs)
{
printf("i = %Iu\n",i);
d1=++i;
printf("d1 = %Iu\n",d1);
pObj=NEW datatype[i]();
if(pObj)
{
if(blnPreserve)
{
size_t iObs=0;
if(i<this->iNumObjects)
iObs=i;
else
iObs=this->iNumObjects;
for(size_t i=0; i<iObs; i++)
pObj[i]=this->pObjs[i];
printf("iObs = %Iu\n",iObs);
}
delete [] this->pObjs;
this->pObjs=pObj;
this->iNumObjects=i;
return true;
}
else
return false;
}
return false;
}
*/
bool Redim(size_t i, bool blnPreserve)
{
datatype* pObj=NULL;
//printf("i = %Iu\n",i);
d1=++i;
//printf("d1 = %Iu\n",d1);
pObj=NEW datatype[i]();
if(pObj)
{
if(blnPreserve)
{
size_t iObs=0;
if(i<this->iNumObjects)
iObs=i;
else
iObs=this->iNumObjects;
//printf("iObs = %Iu\n",iObs);
for(size_t i=0; i<iObs; i++)
{
pObj[i]=this->pObjs[i];
//printf("We Got Inside The If!\n");
}
}
if(this->pObjs)
delete [] this->pObjs;
this->pObjs=pObj;
this->iNumObjects=i;
return true;
}
else
return false;
return false;
}
datatype& operator()(size_t i) // One Dimensional Accessor
{
return pObjs[i];
}
datatype& operator()(size_t i, size_t j) // Two Dimensional Accessor
{
return pObjs[i*d2 + j];
}
datatype& operator()(size_t i, size_t j, size_t k) // Three Dimensional Accessor
{
return pObjs[i*d2 + j + k*d1*d2];
}
datatype& operator()(size_t i, size_t j, size_t k, size_t l) // Four Dimensional Accessor
{
return pObjs[i*d2 + j + k*d1*d2 + l*d1*d2*d3];
}
bool blnMemoryIsGood()
{
return !!pObjs;
}
int UBound(int iDim)
{
if(iDim==1)
return d1-1;
if(iDim==2)
return d2-1;
if(iDim==3)
return d3-1;
if(iDim==4)
return d4-1;
else
return 0;
}
~CArray()
{
if(this->pObjs)
delete [] this->pObjs;
}
private:
datatype* pObjs; // pointer to the base memory allocation for array
size_t iNumObjects; // We'll need this to zero memory for non-class types
size_t d1; // Dimension #1
size_t d2; // Dimension #2
size_t d3; // Dimension #3
size_t d4; // Dimension #4
};
#endif
So that's where my head was at when I started ruminating about C++ vectors. I had a growable container, but performance could be quite terrible if used in the fashion I used it above - as cool as I thought that program was!
So, in thinking about it, and playing around with C++ vectors, I realized for sure their construction was a stack based design. If you do this...
vector<int) v;
printf("v.size() = %u",v.size());
...you'll get zero for size. Same for vector::capacity(). For it to have any size or capacity one must use the vector::push() semantics. And there doesn't appear to be any initial default memory allocation that would enable anything at all to be put within a vector without a first push() operation. So I dug up some of my old template based stack code and came up with this CVector Class...
// cl Test6.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib
#include <windows.h>
#include "stdio.h"
extern "C" int _fltused=1;
template <class type, size_t iSize> class CVector
{
public:
CVector(size_t iSize) : pObjs(NULL)
{
this->m_iSize = iSize;
this->m_Pos = 0;
this->pObjs=new type[this->m_iSize]();
}
bool push(type var)
{
if(m_Pos < m_iSize)
{
pObjs[m_Pos++] = var;
return true;
}
else
{
type* pNewBuffer = new type[m_iSize*2]();
if(pNewBuffer)
{
for(size_t i=0; i<m_iSize; i++)
pNewBuffer[i] = pObjs[i];
delete [] pObjs;
pObjs = pNewBuffer;
pObjs[m_Pos++] = var;
m_iSize = m_iSize*2;
return true;
}
}
return false;
}
type pop()
{
if(m_Pos)
return pObjs[--m_Pos];
else
return false;
}
type& operator()(size_t i)
{
return pObjs[i];
}
~CVector()
{
delete [] this->pObjs;
}
private:
size_t m_Pos;
size_t m_iSize;
type* pObjs;
};
int main()
{
CVector<size_t,10>* pCV = new CVector<size_t,10>(10);
for(size_t i=0; i<25; i++)
printf("pCV->push(%Iu) = %d\ti = %Iu\n",i,pCV->push(i),i);
printf("\n");
for(size_t i=0; i<25; i++)
printf("pCV->pop() = %d\ti = %Iu\n",pCV->pop(),i);
printf("\n");
for(size_t i=0; i<25; i++)
printf("pCV->operator()(%Iu) = %Iu\n",i,pCV->operator()(i));
getchar();
return 0;
}
#if 0
C:\Code\VStudio\VC++9\LibCTiny\x64\Test2>cl Test6.cpp /O1 /Os /GR- /GS- TCLib.lib kernel32.lib
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
Test6.cpp
Microsoft (R) Incremental Linker Version 9.00.21022.08
Copyright (C) Microsoft Corporation. All rights reserved.
/out:Test6.exe
Test6.obj
TCLib.lib
kernel32.lib
C:\Code\VStudio\VC++9\LibCTiny\x64\Test2>test6
pCV->push(0) = 1 i = 0
pCV->push(1) = 1 i = 1
pCV->push(2) = 1 i = 2
pCV->push(3) = 1 i = 3
pCV->push(4) = 1 i = 4
pCV->push(5) = 1 i = 5
pCV->push(6) = 1 i = 6
pCV->push(7) = 1 i = 7
pCV->push(8) = 1 i = 8
pCV->push(9) = 1 i = 9
pCV->push(10) = 1 i = 10
pCV->push(11) = 1 i = 11
pCV->push(12) = 1 i = 12
pCV->push(13) = 1 i = 13
pCV->push(14) = 1 i = 14
pCV->push(15) = 1 i = 15
pCV->push(16) = 1 i = 16
pCV->push(17) = 1 i = 17
pCV->push(18) = 1 i = 18
pCV->push(19) = 1 i = 19
pCV->push(20) = 1 i = 20
pCV->push(21) = 1 i = 21
pCV->push(22) = 1 i = 22
pCV->push(23) = 1 i = 23
pCV->push(24) = 1 i = 24
pCV->pop() = 24 i = 0
pCV->pop() = 23 i = 1
pCV->pop() = 22 i = 2
pCV->pop() = 21 i = 3
pCV->pop() = 20 i = 4
pCV->pop() = 19 i = 5
pCV->pop() = 18 i = 6
pCV->pop() = 17 i = 7
pCV->pop() = 16 i = 8
pCV->pop() = 15 i = 9
pCV->pop() = 14 i = 10
pCV->pop() = 13 i = 11
pCV->pop() = 12 i = 12
pCV->pop() = 11 i = 13
pCV->pop() = 10 i = 14
pCV->pop() = 9 i = 15
pCV->pop() = 8 i = 16
pCV->pop() = 7 i = 17
pCV->pop() = 6 i = 18
pCV->pop() = 5 i = 19
pCV->pop() = 4 i = 20
pCV->pop() = 3 i = 21
pCV->pop() = 2 i = 22
pCV->pop() = 1 i = 23
pCV->pop() = 0 i = 24
pCV->operator()(0) = 0
pCV->operator()(1) = 1
pCV->operator()(2) = 2
pCV->operator()(3) = 3
pCV->operator()(4) = 4
pCV->operator()(5) = 5
pCV->operator()(6) = 6
pCV->operator()(7) = 7
pCV->operator()(8) = 8
pCV->operator()(9) = 9
pCV->operator()(10) = 10
pCV->operator()(11) = 11
pCV->operator()(12) = 12
pCV->operator()(13) = 13
pCV->operator()(14) = 14
pCV->operator()(15) = 15
pCV->operator()(16) = 16
pCV->operator()(17) = 17
pCV->operator()(18) = 18
pCV->operator()(19) = 19
pCV->operator()(20) = 20
pCV->operator()(21) = 21
pCV->operator()(22) = 22
pCV->operator()(23) = 23
pCV->operator()(24) = 24
#endif
And there you should have the solution to your issue James with instantiating a pointer to an aeDymArray object and using it, as in the above code I created and used a pointer to one of my new CVector objects. Not done with this yet by any means.