昨晚写完已断网,故而今早上传。另,可能是这章内容太多,可能是我太过啰嗦,这章居然被我分了五部分。今天或者明天,还会有part V
2012-3-22(Reusing Code in C++part IV)
13. Class Templates
Inheritance and containment aren't always the solution when you want to reuse code. Instead, using class templates are more commonly. Like function templates, templates instructions to the C++ compiler about how to generate class definitions. Unlike function templates, class templates provide parameterized types---that is, the capable of passing type name as an argument to a recipe for building a class.
I. Syntax
A typical class model would like this:
class exam
{
private:
int member;
public:
int function1();
exam function2();
};
Then, if we want to declare a class template,modify it as follows:
template <class Type> //or <typename Type>
class exam
{
private:
Type member;
public:
Type function1();
exam function2();
}
Also,you need to make a little change when defining member functions:
template <class Type>
Type exam<Type>::function1() {/*function body*/}
template <class Type>
exam<Type> exam<Type>::function2() {/*function body*/}
We need to change the class qualifier from
exam::
to
exam<Type>::
. But we can use
exam::
or exam instead of
exam<Type>::
or
exam<Type>
inside the template declaration and template function definitions.
II. Caution
Again, class templates are not class and member function definitions. Rather, they tell the compiler to how to generate a specific class. A particular actualization of a template is called an instantiation or a specialization.Because, the templates aren't functions, placing the template member functions in a separate implementation file won't work, unless you use a compiler that has implemented the new export keyword.
But most of compilers,including Microsoft Visual Studio 2010 Express that I am using are not yet support this keyword (warning in VS2010):
'export' keyword is not yet supported,but reserved for future use
Looking for the reason in the Internet: too complex to implement.
It's strange that when I try to separate declaration and implementation, my compiler reports no problem.I don't know why. For compatibility, we are supposed to place all the template information in a herder file.
14. Design a Simple Array Template
Templates are frequently used for container classes because the idea of type parameters matches well with the need to apply a common storage plan to a variety of types. A typical array template has the following functions (a portion):
- size (); //return the number of elements
- operator [](); //set and get the value of element
We need to specify an array size when creating an array. Two techniques to reach this goal: one is use a dynamic array within the class and a constructor argument to provide the number of elements. Another approach is to use a template argument to provide the size for a regular an ordinary array.
I. Dynamic Array
#ifndef _DARRAY_
#define _DARRAY_
template <typename T>
class DArray
{
private:
int array_size;
T* elems;
public:
DArray (int size = 10); //contructor
DArray (const DArray& da); //copy contructor
~DArray(){delete[] elems;} //destructor
int size() {return array_size;}
const T& operator [](int i) const; //prepare for const objects
T& operator [](int i); //get & set certain element
DArray& operator =(const DArray& da); //assignment operator
};
template <typename T>
DArray<T>::DArray(int size = 10): array_size(size)
{
elems = new T[size];
}
template <typename T>
DArray<T>::DArray(const DArray& da)
{
array_size = da.array_size;
elems = new T[array_size];
for(int i = 0; i< array_size; i++) elems[i] = da.elems[i];
}
template <typename T>
const T& DArray<T>::operator [](int i) const
{
return elems[i];
}
template <typename T>
T& DArray<T>::operator [](int i)
{
return elems[i];
}
template <typename T>
DArray<T>& DArray<T>::operator =(const DArray& da)
{
if(this == &da) return *this;
delete[] elems;
array_size = da.array_size;
elems = new T[array_size];
for(int i = 0; i< array_size; i++)
elems[i] = da.elems[i];
return *this;
}
#endif
Using dynamic array internally, so the class needs a destructor, copy constructor and an assignment operator.
II. Non-Type Arguments
#ifndef _NARRAY_
#define _NARRAY_
template <typename T, int n>
class NArray
{
private:
T elems[n];
public:
NArray() {} //default constructor
//create an object that all elements are e
explicit NArray(const T& e);
int size() const { return n; };
const T& operator [](int i) const;
T& operator [](int i);
};
template <typename T, int n>
NArray<T,n>::NArray(const T& e)
{
for(int i = 0; i< n; i++) elems[i] = e;
}
template <typename T, int n>
const T& NArray<T,n>::operator[](int i)const
{
return elems[i];
}
template <typename T, int n>
T& NArray<T,n>::operator[](int i)
{
return elems[i];
}
#endif
template
heading
template <
typename T, int n>
, whose second kind of parameter specifies a particular type instead of acing as a generic for a type, is called a
non-type
(or
expression
)
argument
.
Non-type arguments have some restrictions. Anon-type can be an integral type, an enumeration type, a reference or a pointer. Also, the templates code can't modify the value of the argument or take its address. In other word, expression like n++ or &n would not be allowed inside the class templates. And when instantiate a template, the value used for the expression argument should be a constant expression.
III. Testing
Two techniques have the same name of interface, so I can use a similar test program.
#include <iostream>
//#include "NArray.h"
#include "DArray.h"
using namespace std;
int main()
{
//NArray<double,7> da(5.0);
DArray<double> da(7);
cout << da.size() << endl;
for(int i = 0; i< 6; i++) da[i] = double(i * 2);
//const NArray<double,7> nda = da;
const DArray<double> nda = da;
for(int i = 0; i< nda.size(); i++)
cout << nda[i] << " ";
cout << endl;
for(int i = 0; i< da.size(); i++)
cout << da[i] << " ";
}
The comment portion is prepare for non-type argument.
VI. Comparison
① Argument approach has less execution time. Constructor approach uses heap memory managed by new and delete, while expression argument uses the memory stack maintained for automatic variables.
② Argument approach costs more resource. For example
NArray<int,7> one;
NArray<int,8> two;
generate two separate class declarations. But
DArray<int> one(7);
DArray<int> two(8);
generate just one separate declaration.
③ Constructor approach is more versatile. Because its array size is stored as a class member. So that, for example, we can assignment from an array of one size to an array of another size or to define a function to resize the array.