Home

Class Templates

 

Introduction

Like a function, a class can be created as a template. In fact, the class is more flexible than the function. To create a class template that uses a constant value for the parameter, use the following formula:

template <PrimitiveDataType ParameterName> Options class ClassName { }

The template and class keywords are required, so are the < and the > operators. Also, the curly brackets that delimit a class must be specified. The Options factor can be an appropriate combination of public, private, ref, and/or value keywords. The class keyword is followed by the name of the class, followed by the delimiting curly brackets of a class.

Practical LearningPractical Learning: Introducing Class Templates

  1. To create a new program, on the main menu, click the File -> New -> Project...
  2. In the Templates list, click CLR Empty Project
  3. Set the name to CommercialStore2
  4. Click OK
  5. On the main menu, click Project -> Add Class
  6. In the Templates list, click C++ Class and click Add
  7. Set the Class Name to CListOfItems and click Finish
  8. Change the header file as follows:
     
    #pragma once
    
    typedef double ItemType;
    
    public value struct CItem
    {
        ItemType Item;
        CItem ^ Next;
    };
    
    public ref class CListOfItems
    {
    private:
        int Size;
    
    public:
        // List Initialization
        CListOfItems(void);
        CItem ^ Head;
    
        // List Finalization
        ~CListOfItems();
        !CListOfItems();
        
        // Operations on list
        int CListOfItems::Count();
        int CListOfItems::Add(CItem ^ item);
        CItem ^ CListOfItems::Retrieve(int Position);
    };
  9. In the Solution Explorer, double-click ListOfItems.cpp and change it as follows:
     
    #include "ListOfItems.h"
    
    CListOfItems::CListOfItems(void)
        : Head(nullptr), Size(0)
    {
    }
    
    CListOfItems::~CListOfItems()
    {
        delete Head;
    }
    
    CListOfItems::!CListOfItems()
    {
    }
    
    int CListOfItems::Count()
    {
        return Size;
    }
    
    int CListOfItems::Add(CItem ^ NewItem)
    {
        CItem ^ Sample = gcnew CItem;
    
        Sample          = NewItem;
        Sample->Next    = Head;
        Head            = Sample;
    
        return Size++;
    }
    
    CItem ^ CListOfItems::Retrieve(int Position)
    {
        CItem ^ Current = Head;
    	
        for(int i = Count() - 1; i > Position && Current != nullptr; i--)
        {
    	Current = Current->Next;
        }
    
        return Current;
    }
  10. On the main menu, click Project -> Add New Item...
  11. In the Templates list, click C++ File (.cpp)
  12. Set the name of the file to Exercise and press Enter
  13. Complete the file as follows:
     
    #include "ListOfItems.h"
    
    using namespace System;
    
    int main()
    {
        CListOfItems ^ Items = gcnew CListOfItems;
        CItem ^ Part;
    
        Part  = gcnew CItem;
        Part->Item = 97.46;
        Items->Add(Part);
    	
        Part  = gcnew CItem;
        Part->Item = 8844.82;
        Items->Add(Part);
    
        Part  = gcnew CItem;
        Part->Item = 108.7873;
        Items->Add(Part);
    
        Part  = gcnew CItem;
        Part->Item = 2764.48;
        Items->Add(Part);
    
        Part  = gcnew CItem;
        Part->Item = 70062.83;
        Items->Add(Part);
    
        Console::WriteLine(L"-=- List of Items -=-");
    
        for(int i = 0; i < Items->Count(); i++)
        {
    	CItem ^ One = Items->Retrieve(i);
    
    	Console::WriteLine(L"Item: {0}", One->Item);
        }
    
        Console::WriteLine(L"\nNumber of Items: {0}\n", Items->Count());
    
        return 0;
    }
  14. Complete the

A Template Parameter as a Constant Type

When creating a class template using our formula, the PrimitiveDataType factor must be one of the integer data types built in the C++ language (short, Byte, char, __wchar_t, etc). It cannot be a floating-point type (float, Single, or double). The data type is followed by a name for the parameter. Outside of the > operator, you can specify such factors as the public, the private, the value, and/or the ref keywords, following the same rules applied to the creation of a class in the previous lessons.

Here is an example of a class:

template <__wchar_t c>
public ref class Description
{
public:
    Description();
    Description(__wchar_t msg)
    {
    }
    ~Description();
    void Show();
};

It's important to remember that, with this class creation, the c parameter is a constant. If you define a method in the body of the class, you can use the template parameter as you see fit. Here is an example:

template <__wchar_t c>
public ref class Description
{
public:
    Description();
    Description(__wchar_t msg)
    {
    }
    ~Description();
    void Show();
};

If you implement a method outside of the body of the class, precede it with the declaration of

template <PrimitiveDataType ParameterName>

Then, after typing the name of the class and before the :: operator, enclose the parameter name within the < and > operators. Here is an example:

template <__wchar_t c>
Description<c>::Description()
{
}

When processing the class, you can involve the template parameter as you want. For example, you can pass it to the (cout class or the) Console::WriteLine() method to display it in the console. You can also involve the parameter in an appropriate operation, such as checking it using a conditional operator. Here is an example:

template <__wchar_t c>
void Description<c>::Show()
{
    switch(c)
    {
    case L'i':
    case L'I':
	Console::WriteLine(L"Integer");
	break;
    case L'c':
    case L'C':
	Console::WriteLine(L"Character");
	break;
    case L'f':
    case L'F':
    case L'd':
    case L'D':
	Console::WriteLine(L"Decimal");
	break;
    }
}

Because the template parameter is treated as a constant, you can formally declare it using the const keyword.

Class Template Declaration

After creating a class template, you can use it by primarily declaring a variable. To declare a variable of a class template in the stack, you can use the following formula:

ClassName<ValueType> VariableName;

The < operator, the > operator, and the ; are required. Like any declaration, you start with the name of the type, in this case the name of the class. Between the angle brackets, specify the same primitive type you used when creating the class. End the stack declaration by the name of the variable. After this declaration, you can use the variable as you see fit. Here is an example:

using namespace System;

template <__wchar_t c>
public ref class Description
{
public:
    Description();
    Description(__wchar_t msg)
    {
    }
    ~Description();
    void Show();
};

template <__wchar_t c>
Description<c>::Description()
{
}

template <__wchar_t c>
Description<c>::~Description()
{
}

template <__wchar_t c>
void Description<c>::Show()
{
    switch(c)
    {
    case L'i':
    case L'I':
	Console::WriteLine(L"Integer");
	break;
    case L'c':
    case L'C':
	Console::WriteLine(L"Character");
	break;
    case L'f':
    case L'F':
    case L'd':
    case L'D':
	Console::WriteLine(L"Decimal");
	break;
    }
}

int main()
{
    // Call the function and pass it an integer value
    Description<L'i'> iDscr;
    iDscr.Show();

    // Call the function and pass it a character value
    Description<L'c'> cDscr;
    cDscr.Show();

    // Call the function and pass it a double-precision value
    Description<L'f'> dDscr;
    dDscr.Show();

    return 0;
}

You can also declare the class variable on the managed heap. To create a handle for the class, in both sides of the assignment operator, type the name of the class followed by the < operator, the type of value, and the > operator. Remember that, for a handle, you must use the ^ and the gcnew operators. Here are two examples:

int main()
{
    // Call the function and pass it an integer value
    Description<L'i'> iDscr;
    iDscr.Show();

    // Call the function and pass it a character value
    Description<L'c'> ^ cDscr = gcnew Description<L'c'>;
    cDscr->Show();

    // Call the function and pass it a double-precision value
    Description<L'f'> ^ dDscr = gcnew Description<L'f'>(L'f');
    dDscr->Show();

    return 0;
}

A Class Template of an Unknown Type

As done for a function, you can create a template without specifying the type of value it would hold. You can declare a member variable of an unknown type. You can pass an unknown type to the methods of a class. To create such a class template, you can use the following formula:

template <class Type> Options class ClassName { }

Once again, the template and class keywords are required, so are the < and the > operators. Inside the <>, after the class keyword, enter a name for the template. The Options factor can be an appropriate combination of public, private, ref, and value keywords. After the name of the class, represented in our formula as ClassName, delimit the body of the class with the curly brackets. In the body of the class, you can do whatever you judge necessary. You can declare a member variable as a template type. You can pass an argument as a template type to one or more of the methods. Here is an example of a class:

template <class TypeOfValue>
public ref class Description
{
public:
    Description();
    Description(TypeOfValue type);
    ~Description();
    void Show();
    String ^ Message;
    TypeOfValue Value;
};

If you define a method in the body of the class, you can use the member variable as you see fit. Here is an example:

template <class TypeOfValue>
public ref class Description
{
public:
    Description();
    Description(TypeOfValue type) : Value(type)
    {
    }
    ~Description();
    void Show();
    String ^ Message;
    TypeOfValue Value;
};

As mentioned earlier, if you want to define a method outside of the body of the class, precede it with the declaration of

template <class Type>

After typing the name of the class and before the :: operator, enclose the template type within the < and > operators. Here is an example:

template <class TypeOfValue>
Description<TypeOfValue>::Description()
{
}

In both cases, whether defining the method in the body of the class or outside, use the member variable. Here is an example:

template <class TypeOfValue>
void Description<TypeOfValue>::Show()
{
    Console::WriteLine(L"{0}: {1}", Message, Value);
}

You can declare a variable of a class template the same way we saw earlier, in the stack or in the managed heap. Here are examples:

using namespace System;

template <class TypeOfValue>
public ref class Description
{
public:
    Description();
    Description(TypeOfValue type) : Value(type)
    {
    }
    ~Description();
    void Show();
    String ^ Message;
    TypeOfValue Value;
};

template <class TypeOfValue>
Description<TypeOfValue>::Description()
{
}

template <class TypeOfValue>
Description<TypeOfValue>::~Description()
{
}

template <class TypeOfValue>
void Description<TypeOfValue>::Show()
{
    Console::WriteLine(L"{0}: {1}", Message, Value);
}

int main()
{
    // Call the function and pass it an integer value
    // Declare the variable on the stack heap
    Description<int> iDscr;
    iDscr.Value = 246;
    iDscr.Message = L"Integer";
    iDscr.Show();

    // Call the function and pass it a character value
    // Declare the variable on the managed heap
    Description<__wchar_t> ^ cDscr = gcnew Description<__wchar_t>;
	
    cDscr->Value = 'G';
    cDscr->Message = L"Character";
    cDscr->Show();

    // Call the function and pass it a double-precision value
    // Declare the variable on the managed heap
    Description<double> ^ dDscr = gcnew Description<double>(355.65);
    dDscr->Message = L"Decimal";
    dDscr->Show();

    return 0;
}

Practical LearningPractical Learning: Using a Non-Integer Template Argument

  1. Access the ListOfItems.h header file and change it as follows:
     
    #pragma once
    
    template <class T>
    public value struct CItem
    {
        T Item;
        CItem ^ Next;
    };
    
    public ref class CListOfItems
    {
    private:
        int Size;
    
    public:
        // List Initialization
        CListOfItems(void);
        CItem<double> ^ Head;
    
        // List Finalization
        ~CListOfItems();
        !CListOfItems();
        
        // Operations on list
        int CListOfItems::Count();
        int CListOfItems::Add(CItem<double> ^ item);
        CItem<double> ^ CListOfItems::Retrieve(int Position);
    };
  2. Access the ListOfItems.cpp source file and change it as follows:
     
    #include "ListOfItems.h"
    
    CListOfItems::CListOfItems(void)
        : Head(nullptr), Size(0)
    {
    }
    
    CListOfItems::~CListOfItems()
    {
        delete Head;
    }
    
    CListOfItems::!CListOfItems()
    {
    }
    
    int CListOfItems::Count()
    {
        return Size;
    }
    
    int CListOfItems::Add(CItem<double> ^ NewItem)
    {
        CItem<double> ^ Sample = gcnew CItem<double>;
    
        Sample          = NewItem;
        Sample->Next    = Head;
        Head            = Sample;
    
        return Size++;
    }
    
    CItem<double> ^ CListOfItems::Retrieve(int Position)
    {
        CItem<double> ^ Current = Head;
    	
        for(int i = Count() - 1; i > Position && Current != nullptr; i--)
        {
    	Current = Current->Next;
        }
    
        return Current;
    }
  3. Access the Exercise.cpp source file and change it as follows:
     
    #include "ListOfItems.h"
    
    using namespace System;
    
    int main()
    {
        CItem<double> ^ item;
        CListOfItems ^ Items = gcnew CListOfItems;
    
    
        item  = gcnew CItem<double>;
        item->Item = 97.46;
        Items->Add(item);
    	
        item  = gcnew CItem<double>;
        item->Item = 8844.82;
        Items->Add(item);
    
        item  = gcnew CItem<double>;
        item->Item = 108.7873;
        Items->Add(item);
    
        item  = gcnew CItem<double>;
        item->Item = 2764.48;
        Items->Add(item);
    
        item  = gcnew CItem<double>;
        item->Item = 70062.83;
        Items->Add(item);
    
        Console::WriteLine(L"-=- List of Items -=-");
    
        for(int i = 0; i < Items->Count(); i++)
        {
    	CItem<double> ^ One = Items->Retrieve(i);
    
    	Console::WriteLine(L"Item: {0}", One->Item);
        }
    
        Console::WriteLine(L"\nNumber of Items: {0}\n", Items->Count());
    
        return 0;
    }
  4. Execute the application to see the result:
     
    -=- List of Items -=-
    Item: 97.46
    Item: 8844.82
    Item: 108.7873
    Item: 2764.48
    Item: 70062.83
    
    Number of Items: 5
    
    Press any key to continue . . .
  5. Close the DOS window

The Type of a Template

We have seen that a template could be created using the class keyword. As an alternative, you can use the typename keyword instead of class. Here is an example:

using namespace System;

template <typename TypeOfValue>
public ref class Description
{
public:
    Description();
    Description(TypeOfValue type) : Value(type)
    {
    }
    ~Description();
    void Show();
    String ^ Message;
    TypeOfValue Value;
};

template <typename TypeOfValue>
Description<TypeOfValue>::Description()
{
}

template <typename TypeOfValue>
Description<TypeOfValue>::~Description()
{
}

template <typename TypeOfValue>
void Description<TypeOfValue>::Show()
{
    Console::WriteLine(L"{0}: {1}", Message, Value);
}

int main()
{
    // Call the function and pass it an integer value
    // Declare the variable on the stack heap
    Description<int> iDscr;
    iDscr.Value = 246;
    iDscr.Message = L"Integer";
    iDscr.Show();

    // Call the function and pass it a character value
    // Declare the variable on the managed heap
    Description<__wchar_t> ^ cDscr = gcnew Description<__wchar_t>;
	
    cDscr->Value = 'G';
    cDscr->Message = L"Character";
    cDscr->Show();

    // Call the function and pass it a double-precision value
    // Declare the variable on the managed heap
    Description<double> ^ dDscr = gcnew Description<double>(355.65);
    dDscr->Message = L"Decimal";
    dDscr->Show();

    return 0;
}

Based on this, the class keyword used for a template creation can be replaced with typename.

 

Previous Copyright © 2006-2016, FunctionX, Inc. Next