None type template parameters
Here is a class for generating random numbers, which can accept a number and then generate a random number that meets the requirements by overloading () symbols. The specific code is as follows:
//: c03: urand.h
// Unique Random Number Generator
#ifndef urand_h
#define urand_h
#include
#include
Template
Class URAND
{
INT used [Upperbound];
Bool recycle;
PUBLIC:
URAND (Bool Recycle = FALSE);
Int operator () (); // the "generator" function
}
Template
URAND
: Recycle (Recyc)
{
MEMSET (Up, 0, Upperbound * SizeOf (int));
SRAND (Time (0)); // SEED Random Number Generator
}
Template
INT URAND
{
IF (! Memchr (Used, 0, UpperBound))
{
IF (Recycle)
MEMSET (USED, 0, SIZEOF (INT);
Else
Return -1; // no more spaces Left
}
Int newval;
While (used [newval = rand ()% Upperbound])
; // Until Unique Value Is Found
Used [newval] ; // set flag
Return NewVal;
}
#ndif // urand_h ///: ~
The class urand works is this: it retains a number of MAP (mappings) that can be taken in the subsequent space (the upper limit of the random number is passed to the template), and the used digital is tagged. Controllable constructor allows you to recycle reuse after all resources have been used. Note: This class is to implement functionality from the perspective of optimization speed, so it allocates mapping space for all entities.
Default template parameters
Use of keyword Typename
Look at the following code:
//: c03: typendid.cpp
// using 'TypenAme' to Say's A Type,
// and not something Other Than A Type
Template
{
// without typeename, you shouth get an error:
TYPENAME T :: ID i;
PUBLIC:
Void f () {i.g ();
}
Class Y
{
PUBLIC:
Class ID
{
PUBLIC:
Void g () {}
}
}
int main ()
{
Y y;
X
XY.F ();
} ///: ~
As can be seen from the definition in the template: We assume that the class T can also be used to declare the variable within the class (Type) - ID in the class T. The ID can be an object in this class T, usually, you can do it directly to the ID, but you can't use ID Creat one other object. However, here, with the help of Typename, we have done this. The ID of the program is treated as a type that is indeed true, but if we throw the TypenAme keyword, the compiler can't know what the ID is something. (When there is no Typename, the compiler will choose and distinguish what type is what we define in the template, so, it will regard the ID as other things instead of a type (Type), in other words: it Always more willing to see the marking as an object (even variable private type), an enumeration or what a similar statement. Of course, it won't - see it as a (type) Type, it is not so smart, when we use the ID as a TYPE, the compiler will not understand.
The TypenAme keyword tells the compiler to explain a special name into a type, and you must use the TypenAme keyword to a NAME in the following cases:
1. A unique name (can be understood as a type of understanding), it is nested in another type.
2. Depending on a template parameter, it is said that template parameters contain this Name to some extent. When the template parameter makes the compiler have misunderstand when a type is recognized.
During insurance, you should use TypeName as a variable in all compilers. Just like the T :: ID in the example above, because we use Typename, so the compiler knows that it is a type, which can be used to declare and create an instance.
Give you a concise usage guide: If your type is limited in the template parameter, you must use Typename.
Customize a type with Typename
To know the TypeName keyword not automatically type,
Typename SEQ :: item.
Just declare a variable of a SEQ :: Iterator type, if you want to define a new type, you have to do this:
TypeDef Typename SEQ :: item.
Use Typename to replace CLASS
After detailing the use of Typename, we can now select TypenAme to replace the Class declaration, which can increase the clarity of the program.
//: c03: usingtypename.cpp
// using 'Typename' in The Template Argument List
Template
int main ()
{
X
} ///: ~
You will of course see that many similar code do not use the TypenAme keyword, because the template concept has been born for a long time, there is a TypenAme keyword.
Function template
A template class describes a collection of unlimited classes that you see is the most common place in these classes. Of course, C also supports the concept of unlimited collection functions, which is very useful, these things are essentially the same, unless you want to declare a function instead of a class.
You may already know that we have to create a template function, that is, because we find that many functions look exactly the same, except for the types they have different. Also, a function template is very useful in many places, just as we will see in the first example and we will see the second example, which uses the container (Containers) and states. String conversion system
//: c03: Stringconv.h
// Chuck Allison's String Converter
#ifndef stringconv_h
#define stringconv_h
#include
#include
Template
T fromString (Const std :: string & s)
{
Std :: istringstream IS (s);
T t;
IS >> T;
Return T;
}
Template
Std: String Tostring (Const T & T)
{
Std :: ostringstream s;
S << T;
Return S.STR ();
}
#ENDIF // StringConv_H ///: ~
Here is the test program, which includes the use of standard library complex:
//: c03: StringConvtest.cpp
#include "stringconv.h"
#include
#include
Using namespace std;
int main ()
{
INT I = 1234;
COUT << "i == /" "<< Tostring (i) <<" / "/ n";
Float x = 567.89;
Cout << "x == /" "<< toString (x) <<" / "/ n";
Complex
Cout << "c == /" "<< Tostring (c) <<" / "/ n";
Cout << Endl;
I = fromString
Cout << "i ==" << i << endl;
X = fromString
COUT << "x ==" << x << endl;
C = fromString
COUT << "c ==" << c << endl;
} ///: ~
The output result is:
i == "1234"
X == "567.89"
c == "(1, 2)"
i == 1234
X == 567.89
C == (1, 2)
Memory allocation system
In the topic of Malloc (), Calloc () and Realloc (), we have a lot of things, and the next function template processes a function getMem (), this function can be assigned new Memory space, or adjust to allocate the size of the memory space, which set all the new space 0 and check if the operation is successful. In this way, you only need to tell how many space needs it, and it is possible to reduce the possibility of an error.
//: c03: getMem.h
// Function Template for Memory
#ifndef getmem_h
#define getmem_h
#include "../require.h"
#include
#include
Template
Void getMem (T * & OldMem, Int Elems)
{
Typedef int CNTR; // Type of Element Counter
Const int CSZ = SizeOf (CNT); // and size
Const Int Tsz = SizeOf (T);
IF (Elems == 0)
{
Free (& (((cntr *) oldmem [- 1]));
Return;
}
T * p = OldMem;
CNTR Oldcount = 0;
IF (p)
{
// previously allocated memory
// Old Style:
// ((cntr *) p) -; // back up by one CNTR
// new style:
CNTR * TMP = Reinterpret_cast
P = reinterpret_cast
Oldcount = * (cntr *) p; // previous # elems
}
T * m = (t *) Realloc (p, elems * tsz csz);
Require (m! = 0);
* ((cntr *) m) = elems; // Keep TRACK OF Count
Const cntr increment = elems - Oldcount;
IF (Increment> 0)
{
// Starting Address of Data:
Long StartAdr = (long) & (M [Oldcount]);
STARTADR = CSZ;
// Zero The Additional New Memory:
MEMSET (Void *) StartADR, 0, Increment * TSZ);
}
// Return The Address Beyond The Count:
Oldmem = (t *) & ((cntr *) m) [1]);
}
Template
Inline void freemem (t * m) {getMem (m, 0);}
#ENDIF // getMem_H ///: ~
In order to be able to empty the new memory space, the program allocates a counter to record how many memory blocks are assigned, TypeDef CNTR is the type of this counter.
There is a pointer reference (OldMem), because when we assign a new memory space, the original memory head pointer changes, this can help us find the pointer. If the parameter passes 0, this memory is released, which is brought by additional function freeMem ().
You will find that getMem's operation is quite underlying, there are many types and bytes of operations, for example, pointer Oldmem does not point to the start space of memory, which puts the starting space of the memory to the counter. So, when we want free () this memory, getMem () must reverse the number of bytes occupied by this pointer CNTR, because the OldMem is a T *, which must first be converted into a CNTR *, then the index reverses a position, and finally Execute free ():
Free (& (((cntr *) oldmem [- 1]));
Similarly, if multiple memory is pre-allocated, getMem () must first get the current memory allocation, and then recalculate the method of calling the realloc (). If the size is increased, in order to clear the new address space, we must calculate, using the start address of the MEMSET, finally, the address of the OldMem is still the address space of the counter.
Oldmem = (t *) & ((cntr *) m) [1]);
Rea: because OldMem is a reference to the pointer, it will change any parameters from the outside world.
Here is a program that test getMem ():
//: c03: getMem.cpp
// Test Memory Function Template
#include "getMem.h"
#include
Using namespace std;
int main ()
{
INT * P = 0;
GetMem (p, 10);
For (int i = 0; i <10; i )
{
Cout << p [i] << ';
P [I] = i;
}
Cout << '/ n';
GetMem (p, 20);
For (int J = 0; j <20; j )
{
COUT << p [j] << ';
p [j] = j;
}
Cout << '/ n';
GetMem (p, 25);
For (int K = 0; k <25; k )
COUT << p [k] << '';
FreeMem (p);
Cout << '/ n';
Float * f = 0;
GetMem (f, 3);
For (int u = 0; u <3; u )
{
COUT << F [u] << '';
F [u] = u 3.14159;
}
Cout << '/ n';
GetMem (f, 6);
For (int V = 0; v <6; v )
Cout << f [v] << '';
FreeMem (f);
} ///: ~