C++ Generic Programming (Template)

Posted by shavas on Wed, 22 May 2019 00:08:03 +0200

1. Overview of Templates

background

  • Sometimes the logical structure of many functions or subroutines is the same, but the data types to be processed are different.
  • Sometimes many classes have the same logical member function and member variable, but the data type of member variable and the parameter type of member function are different.
  • Template is a mechanism to solve code redundancy caused by inconsistent data types. Essentially, it is parameterization of data types, abstracting functions or classes corresponding to various data types with a logical structure.

2. Function Template

2.1 Basic Grammar

Sample code

#include <iostream>
using namespace std;

template <typename T>//Template description
T myFunc(T size)//Function realization
{
    cout << "size:" <<size<< endl;
    return size;
}

int main(void)
{


    myFunc(8);//Automatic Derivation Call
    myFunc<float>((float)12.9);//Display invocation
    cout << "Hello!" << endl;
    //system("pause");
    return 0;
}
  • The generic parameters in the template description must be used in the function definition, but common types can not be used.
  • Template descriptions can be done using multiple type parameters
#include <iostream>
using namespace std;

template <typename T1, typename T2>//Template description, two type parameters
T2 mySort(T1 * array,T2 length)
{
    T2 i = 0, j = 0;
    T1 tmp = array[0];
    for (i = 0; i < length; i++)
    {
        for (j = i + 1; j < length; j++)
        {
            if (array[i] < array[j])
            {
                tmp = array[i];
                array[i] = array[j];
                array[j] = tmp;
            }
        }
    }

    return i;
}

template <typename T1,typename T2>//Template description
T2 printArray(T1 *array,T2 length)//Function realization
{
    T2 i = 0;
    for (i = 0; i < length; i++)
    {
        cout << (int)array[i]<<""<< endl;
    }
    cout << endl;
    return i;
}



int main(void)
{

    char myArray[10] = {1,3,87,54,98,37,33,63,89,2};
    int size = sizeof(myArray) / (*myArray);

    mySort(myArray, size);

    printArray(myArray, size);
    cout << "Hello!" << endl;
    //system("pause");
    return 0;
}

2.2 Function Template Meets Function Overload

Conclusion:
Function templates do not allow automatic type conversion
Ordinary functions can perform automatic type conversion

Invocation rule
1 Function templates can be overloaded like normal functions
2 C++ Compiler Gives Priority to Common Functions
3 If the function template can produce a better match, then select the template
4. You can restrict the compiler to match only through templates by using the syntax of empty template argument lists.

/*
Function templates do not allow automatic type conversion
 Ordinary functions can perform automatic type conversion
*/

/*
    1 Function templates can be overloaded like normal functions
    2 C++Compilers give priority to common functions
    3 If the function template can produce a better match, then select the template
    4 Compilers can be limited to template matching only by the syntax of empty template argument lists
*/


#include "iostream"
using namespace std;


int Max(int a, int b)
{
    cout<<"int Max(int a, int b)"<<endl;
    return a > b ? a : b;
}

template<typename T>
T Max(T a, T b)
{
    cout<<"T Max(T a, T b)"<<endl;
    return a > b ? a : b;
}

template<typename T>
T Max(T a, T b, T c)
{
    cout<<"T Max(T a, T b, T c)"<<endl;
    return Max(Max(a, b), c);
}


void main()
{
    int a = 1;
    int b = 2;

    cout<<Max(a, b)<<endl; //When the function template and the ordinary function conform to the call, the ordinary function should be chosen first.
    cout<<Max<>(a, b)<<endl; //If the display uses function templates, use the <> type list

    cout<<Max(3.0, 4.0)<<endl; //Use function templates if they produce better matches

    cout<<Max(5.0, 6.0, 7.0)<<endl; //heavy load

    cout<<Max('a', 100)<<endl;  //Implicit type conversion can be achieved by calling ordinary functions 
    system("pause");
    return ;
}

2.3 Function Template Implementation Mechanism

Introduction to Compiler

  • The author of the gcc (GNU C Compiler) compiler is Richard Stallman and the founder of the GNU project.
  • What is gcc: GCC is the abbreviation of GNU Compiler Collection. Originally as a compiler of C language (GNU C Compiler), it now supports many languages, such as C, C++, Java, Pascal, Ada, COBOL and so on.
  • gcc supports a variety of hardware platforms, and even provides perfect support for unusual computers such as Don Knuth's MMIX.

Main characteristics of gcc

1) gcc is a portable compiler that supports multiple hardware platforms
2) gcc is not only a local compiler, but also a cross-compiler across platforms.
3) gcc has a multilingual front-end for parsing different languages.
4) gcc is modular and can be supported by new languages and new CPU architectures
5) gcc is free software

gcc compilation process

  • Pre-Processing
  • Compiling
  • Assembling
  • Linking
Gcc *.c –o 1exe (Overall compilation steps)
Gcc –E 1.c –o 1.i  //Macro Definition Macro Expansion
Gcc –S 1.i –o 1.s 
Gcc –c 1.s –o 1.o  
Gcc 1.o –o 1exe

Conclusion: gcc compiler is a tool chain...

Common compilation options for GCC

option Effect
-o Generate targets (.i,.s,.o, executable files, etc.)
-c Notify gcc to cancel the link step, which is to compile the source code and generate the target file at the end
-E Run only the C precompiler
-S Tell the compiler to stop compiling after the assembly language file is generated, and the generated assembly language file is extended to.s.
-Wall Cause gcc to warn about code problems in source files
-Idir Add the dir directory to the directory path of the search header file
-Ldir Add dir directory to directory path of search library
-llib Link lib Library
-g Embedding debugging information in the target file for debugging debuggers like gdb

Common compilation steps of GCC

1. gcc-E hello.c-o hello.i (pretreatment)
2.gcc-S hello.i-o hello.s (compiled)
3. gcc-c hello.s-o hello.o (assembly)
4.gcc hello.o -o hello (link)
These four steps can be combined into one step.
GCC hello.c-o Hello (direct compilation links to executable target files)
Gcc-c hello.c or gcc-c hello.c-o hello.o

Gcc Compiles Multiple Files

hello_1.h
hello_1.c
main.c

One-time compilation

gcc  hello_1.c main.c –o newhello

Independent compilation

gcc -Wall -c main.c -o main.o
gcc -Wall -c hello_1.c -o hello_fn.o
gcc -Wall main.o hello_1.o -o newhello

Disassembly observation

  • The compiler does not process function templates into functions that can handle any class
  • The compiler generates different functions from function templates by specific types
  • The compiler compiles the function template twice
    • Compile the template code itself where it is declared;
    • Compile the code where the parameter is replaced.

3. types of templates

Class templates are similar to function templates in definition and usage, as we have described. Sometimes, there are two or more classes whose functions are the same, but the data types are different. A class is declared as follows:

Class templates are used to parameterize the type of data required by a class

Class templates are particularly important in representing data structures such as arrays, tables and graphs.
The representation and algorithm of these data structures are not affected by the type of elements they contain.

When declaring objects with class templates, the specific types of specified formal parameters should be displayed so that the C++ compiler can allocate specific memory to the objects.

3.1 Grammar of Common Class Templates

Grammar of a single class

#include <iostream>
using namespace std;

template <typename T>
class A
{
public:
    A(int a = 0)
    {
        this->a = a;
    }
    void printA()
    {
        cout << a << endl;
    }
protected:
private:
    T a;
};

int main(void)
{

    A<int> a1;
    a1.printA();

    A<int> a2(19);
    a2.printA();
    cout<<"Hello!"<<endl;

    return 0;
}

Template class as function parameter

#include <iostream>
using namespace std;

template <typename T>
class A
{
public:
    A(int a = 0)
    {
        this->a = a;
    }
    void printA()
    {
        cout << a << endl;
    }
protected:
private:
    T a;
};


//Class template as function parameter

//Parameters, C++ compiler requires specific classes, so A<int> &a is required. 
void UseA(A<int> &a)
{
    a.printA();
}

int main(void)
{

    A<int> a1;
    UseA(a1);

    A<int> a2(19);
    UseA(a2);
    cout<<"Hello!"<<endl;

    return 0;
}

3.2 Inheritance Class Template Syntax

When template classes are derived, template classes need to be specified. The C++ compiler needs to know exactly what the data type of the parent class looks like. You need to know what the memory size of the parent class is. Only when the data type is fixed can we know how to allocate memory.

When template classes are derived, template classes need to be specified. The C++ compiler needs to know exactly what the data type of the parent class looks like. You need to know what the memory size of the parent class is. Only when the data type is fixed can we know how to allocate memory.

Derivation of generic classes from template classes

#include <iostream>
using namespace std;

template <typename T>
class A
{
public:
    A(int a = 0)
    {
        this->a = a;
    }
    void printA()
    {
        cout << a << endl;
    }
protected:
    T a;
private:

};

class B:public A<int>
{
public:
    B(int a = 10, int b = 20) : A<int>(a)
    {
        this->b = b;
    }
    void printB()
    {
        cout << "a:" << a << " b: " << b << endl;
    }
protected:

private:
    int b;
};

int main(void)
{
    B  b1(1, 2);
    b1.printB();
    cout<<"Hello!"<<endl;

    return 0;
}

Derivation of template classes from template classes

#include <iostream>
using namespace std;

template <typename T>
class A
{
public:
    A(int a = 0)
    {
        this->a = a;
    }
    void printA()
    {
        cout << a << endl;
    }
protected:
    T a;
private:

};

template <typename T>

class C :public A<T>
{
public:
    C(T a, T c) :A<T>(a)
    {
        this->c = c;
    }
    void printC()
    {
        cout << "a:" << a << "c:" << c << endl;
    }
private:
    T c;
protected:
};

int main(void)
{

    C<int> c1(1, 2);
    c1.printC();
    cout<<"Hello!"<<endl;

    return 0;
}

3.2 Type Template Knowledge System

Class template functions are written entirely inside the class

#include <iostream>
using namespace std;

template <typename T>
class Complex
{
    friend Complex MySub(Complex c1, Complex c2)
    {
        Complex tmp(c1.a-c2.a,c1.b-c2.b);
        return tmp;
    }
    friend ostream & operator<<(ostream & out, Complex & c)
    {
        out << c.a << " + " << c.b << "i" << endl;
        return out;
    }
public:
    Complex(T a, T b)
    {
        this->a = a;
        this->b = b;
    }

    Complex operator+(Complex & c2)
    {
        Complex tmp(a + c2.a,b + c2.b);
        return tmp;
    }
    void printCom()
    {
        cout << a << " + " << b << "i" << endl;
    }
protected:
private:
    T a;
    T b;
};

int main(void)
{

    //Template classes need to be specified before the object C++ compiler can be defined to allocate memory
    Complex<int>    c1(1, 2);
    Complex<int>    c2(3, 4);

    Complex<int> c3 = c1 + c2;
    //c3.printCom();
    cout << c3 << endl;

    //Abuse of friend function
    {
        Complex<int> c4 = MySub(c1, c2);
        cout << c4 << endl;
    }
    cout<<"Hello!"<<endl;

    return 0;
}

Class template functions are written entirely outside the class, but in the same cpp

If a template class has a friend function and the parameters of the friend function contain template class objects, the pre-declaration of class template and friend function is needed.
It should be noted that:

  1. Pre-declaration is required for friend functions in template classes and template classes
  2. Friend functions do not specify specific data types after function names when they are declared in advance, but when they are declared in template classes, they need to be followed by specific data types, such as < T >.
  3. When member functions of template classes are implemented outside of classes, attention should be paid to parameter lists, function names (class scopes) and return types to specify specific data types forcibly so that the compiler can determine the allocation of memory.
  4. When a friend function is implemented outside the class, it does not need to manage the scope of the class in front of the function name and the specific data type behind the function name. It only needs to pay attention to the list of parameters and whether the return value needs to specify the data type.
  5. When all functions are implemented outside the class, there is no need to care about the specific data type after the function name.
  6. When calling a friend function, you need to follow the specific data type immediately after the function name. Implicitly, the pre-declaration format is the same as the external implementation format. When the class is declared and invoked internally, the function name is followed by a specific data type.
#include <iostream>
using namespace std;

template <typename T>//Pre-declaration of class
class Complex;

template <typename T>//Pre-declaration of friend function
Complex<T> MySub(Complex<T>& c1, Complex<T>& c2);//There is no need to specify a specific data type after the function name when the prefix declaration of a friend function is made

template <typename T>
ostream& operator<<(ostream& out, Complex<T>& c);//There is no need to specify a specific data type after the function name when the prefix declaration of a friend function is made


template <typename T>
class Complex
{
    friend Complex<T> MySub<T>(Complex<T>& c1, Complex<T>& c2); //When declaring a friend function in a template class, you need to follow the specific data type immediately after the function name, such as <T>.
    friend ostream & operator<<<T>(ostream & out, Complex<T> & c);//When declaring a friend function in a template class, you need to follow the specific data type immediately after the function name, such as <T>.

    /*
    friend ostream & operator<<(ostream & out, Complex<T> & c);
    If there is no < T > after operator, the error will be reported, so when there is a friend function in the template class,
    In addition to the unexpected pre-declaration of friend functions, we also need to specify the data type of function names in template classes.
    That is, specify a specific type name parameter type that follows the function name.
    */
public:
    Complex(T a, T b);
    Complex operator+(Complex & c2);
    void printCom();
protected:
private:
    T a;
    T b;
};


/*
When a member function of a template class is implemented outside the class,
Attention should be paid to the list of parameters, the name of the function (class scope) and whether the return type needs to be forced to specify the specific data type.
For the compiler to determine the allocation of memory
*/
template <typename T>
Complex<T>::Complex(T a, T b)
{
    this->a = a;
    this->b = b;
}

template <typename T>
Complex<T> Complex<T>::operator+(Complex<T> & c2)
{
    Complex<T> tmp(a + c2.a, b + c2.b);
    return tmp;
}

template <typename T>
void Complex<T>::printCom()
{
    cout << a << " + " << b << "i" << endl;
}

/*
When a friend function is implemented outside the class,
There is no need to manage the class scope before the function name and the specific data type after the function name.
Just pay attention to the parameter list and whether the return value needs to specify the data type.
*/
template <typename T>
Complex<T> MySub(Complex<T>& c1, Complex<T>& c2)
{
    Complex<T> tmp(c1.a - c2.a, c1.b - c2.b);
    return tmp;
}

template <typename T>
ostream & operator<<(ostream & out, Complex<T> & c)
{
    out << c.a << " + " << c.b << "i" << endl;
    return out;
}

int main(void)
{
    //Template classes need to be specified before the object C++ compiler can be defined to allocate memory
    Complex<int>    c1(1, 2);
    Complex<int>    c2(3, 4);

    Complex<int> c3 = c1 + c2;
    //c3.printCom();
    cout << c3 << endl;

    //Abuse of friend function
    {
        Complex<int> c4 = MySub<int>(c1, c2);//When calling a friend function, you need to follow the specific data type immediately after the function name.
        cout << c4 << endl;

    }

    cout<<"Hello!"<<endl;

    return 0;
}

CONCLUSION: Do not abuse the friend function. The general friend function only applies to overloaded or > operators.

Class template functions are written entirely outside the class, but in different. h and cpp

Since the implementation mechanism of template is essentially two compilations, if only the header file (declaration of class template) is included in the main program, the compiler will not automatically find the function body of member function and friend function in the CPP file. So there's an error that you can't find a function body. It can only be a cpp file that contains the implementation function body, and the cpp file contains the H file, so it essentially contains the declaration of class template and the implementation of class template function. Therefore, the industry writes these two parts (. h and. cpp) in the same file, called hpp file. It only needs to include the hpp file in the provided open source library, so the class template can be used.

  • Header files of traditional classes (class template declaration part)
#pragma  once

#include <iostream>
using namespace std;

template <typename T>
class Complex
{
    friend ostream & operator<< <T> (ostream &out, Complex &c3);

public:
    Complex(T a, T b);
    void printCom();
    Complex operator+ (Complex &c2);

private:
    T   a;
    T   b;
};
  • Implementation of traditional classes (implementation of template functions for specific classes)
#include <iostream>
using namespace std;
#include "complex.h"


//The implementation of the constructor is written outside the class
template <typename T>
Complex<T>::Complex(T a, T b)
{
    this->a = a;
    this->b = b;
}

template <typename T>
void Complex<T>::printCom()
{
    cout << "a:" << a << " b: " << b << endl;
}


template <typename T>
Complex<T>  Complex<T>::operator+ (Complex<T> &c2)
{
    Complex tmp(a + c2.a, b + c2.b);
    return tmp;
}


template <typename T>
ostream & operator<<(ostream &out, Complex<T> &c3)
{
    out << c3.a << " + " << c3.b << "i" << endl;
    return out;
}
  • Test program (including hpp file)
#include <iostream>
using namespace std;
#include "complex.cpp"

void main()
{

    //Template classes need to be specified before the object C++ compiler can be defined to allocate memory
    Complex<int>    c1(1, 2);
    Complex<int>    c2(3, 4);

    Complex<int> c3 = c1 + c2;
    cout << c3 << endl;


    cout << "hello..." << endl;
    return;
}

static keywords in class 3.3 templates

  • Class template - > instantiation - > template class
    Each template class has its own class template data member, and all objects of the template class share a static data member.
  • Like the static data members of non-template classes, the static data members of template classes should also be defined and initialized in the file scope.
  • Each template class has its own copy of the static data members of the class template
/*

The compiler does not process function templates into functions that can handle any class
    The compiler generates different functions from function templates by specific types
    The compiler compiles the function template twice
    The template code itself is compiled where it is declared; the code after parameter substitution is compiled where it is invoked.
*/

#include <iostream>
using namespace std;

template <typename T>
class AA
{
public:
    static T m_a;
protected:
private:
};

template <typename T>
T AA<T>::m_a  = 0;


class AA1
{
public:
    static int m_a;
protected:
private:
};
 int AA1::m_a  = 0;


class AA2
{
public:
    static char m_a;
protected:
private:
};
char AA2::m_a  = 0;




void main()
{
    AA<int> a1, a2, a3;
    a1.m_a = 10;
    a2.m_a ++;
    a3.m_a ++;
    cout << AA<int>::m_a << endl;

    AA<char> b1, b2, b3;
    b1.m_a = 'a';
    b2.m_a ++;
    b2.m_a ++ ;

    cout << AA<char>::m_a << endl;

    //M_a should be each type of class using its own m_a


    cout<<"hello..."<<endl;
    system("pause");
    return ;
}

Summary of 3.4 Types of Templates

Declarations of class templates

1. Write an actual class first. Owing to its clear semantics and clear meanings, it will not make mistakes in general.
2. Change the type name (such as int to float or char) in this class to a virtual type name (such as numtype in the example above).
3. Add a line before the class declaration in the form of (class and typename work the same):
Template < class virtual type parameter >
Such as:

 template <class numtype> //Note that there is no semicolon at the end of the line
 class Compare
 {...}; //Class body

4. When defining objects with class templates, use the following forms:
Class template name < actual type name > object name;
Class template name < actual type name > object name (argument column);
Such as:

 Compare<int> cmp;
 Compare<int> cmp(3,7);

5. If a member function is defined outside a class template, it should be written in the form of a class template:
Template < class virtual type parameter >
Function Type Class Template Name < Virtual Type Parameters >: Member Function Name (Function Parametric Table Column) { }
6. A class template can have one or more type parameters. Each type must be preceded by a class or a typename, such as:

template <class T1,class T2>
class someclass
{...};

When defining an object, the actual type names are substituted, such as:
someclass<int,double> obj;
7. As with classes, when using class templates, we should pay attention to their scope of action, and only use them to define objects in their valid scope.
8. Templates can be hierarchical. A class template can be used as a base class to derive a derived template class.

4. Application of Template in Engineering

Overview

1. Templates are polymorphic tools for type parameterization in C++. They provide function templates and class templates.
2. Template definitions begin with template descriptions, and class parameters must be used at least once in template implementations.
3. The same generic parameter can be used for multi-class templates.
4. Class parameters can be used for function parameters, return types and variables in declared functions.
5. Template is instantiated by the compiler according to the actual data type to generate the actual executable code, thus obtaining template function and template class.
6. Function templates can be overloaded
7. Class templates can be derived and inherited

Containers used in Engineering
All containers provide value semantics, not reference semantics. When the container performs the insertion of elements, the copy action is implemented internally. So elements stored in STL containers must be able to be copied (copy constructors must be provided).

case
Designing an array template class (MyVector) to complete the management of int, char, Teacher type elements.

  • Class template definition
  • Constructor
  • copy constructor
  • Overload operator < []= operator
  • Derivation from an array template

Use basic data types and general custom class objects as container elements

  • Container/Array Class Template Header File
#pragma once        // Make sure that the header file is compiled only once.

#include <iostream>
using namespace std;


template <typename T>
class MyVector
{
public:
    MyVector(int size = 0);//Constructor
    MyVector(const MyVector & obj);//copy constructor
    ~MyVector();//Destructor

    T & operator[](int index);
    MyVector & operator=(MyVector obj);
    int getLen();

    friend ostream & operator<< <T>(ostream & out, const MyVector<T> & obj);
protected:
private:
    int m_len;
    T *m_space;
};
  • Container/Array Class Template Implementation File
#include "myvector.h"

template <typename T>
T & MyVector<T>::operator[](int index)
{
    return m_space[index];
}

template <typename T>
MyVector<T>::MyVector(int size = 0)//Constructor
{
    m_space = new T[size];
    m_len = size;
}

template <typename T>
MyVector<T>::MyVector(const MyVector & obj)//copy constructor
{
    m_len = obj.m_len;
    m_space = new T[m_len];

    for (int i = 0; i < m_len; i++)
    {
        m_space[i] = obj.m_space[i];
    }

}

template <typename T>
MyVector<T>::~MyVector()//Destructor
{
    if (m_space != NULL)
    {
        delete[]m_space;
        m_len = 0;
        m_space = NULL;
    }
}


template <typename T>
MyVector<T> & MyVector<T>::operator=(MyVector<T> obj)
{
    /*1.Release old memory*/
    if (m_space != NULL)
    {
        delete[]m_space;
        m_space = NULL;
        m_len = 0;
    }

    /*2.Reallocation of memory*/
    m_space = new T[obj.m_len];
    m_len = obj.m_len;

    /*3.Copy data*/
    for (int i = 0; i < m_len; i++)
    {
        m_space[i] = obj[i];
    }

    /*4.Returns the left value itself*/
    return  *this;
}

template <typename T>
int MyVector<T>::getLen()
{
    return m_len;
}

template <typename T>
ostream & operator<<(ostream & out, const MyVector<T> & obj)
{
    for (int i = 0; i < obj.m_len; i++)
    {
        cout << obj.m_space[i] << " ";
    }
    cout << endl;
    return out;
}
  • Container/Array Class Test File
#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include "myvector.cpp"//Note that the cpp file is included
using namespace std;


class Teacher
{
public:
    Teacher()
    {
        age = 22;
        strcpy(name, "Goopher");
    }
    Teacher(int age, char *name)
    {
        this->age = age;
        strcpy(this->name,name);
    }

    void printTeacher()
    {
        cout << name << "," << age << endl;
    }
protected:
private:
    int age;
    char name[32];
};

class Test1
{
public:
protected:
private:
    int a;
};

class Test2
{
public:
protected:
private:
    int a;
    static int b;//static members are stored in the global area and do not occupy the memory model of class objects.
};

class Test3
{
public:
    virtual void hello()//Virtual functions make class objects have VPTR pointers, which take up one more pointer size.
    {

    }
protected:
private:
    int a;
    static int b;
};

class Test4
{
public:
    virtual void hello()
    {

    }

    virtual void hello01() = 0;//Multiple virtual functions correspond to only one VPTR pointer (a virtual function table)
protected:
private:
    int a;
    static int b;
};

class Test5
{
public:
    virtual void hello()
    {

    }

    virtual void hello01() = 0;

    void printTest()//Nor does ordinary member functions occupy the memory model of class objects
    {

    }
protected:
private:
    int a;
    static int b;
};

class Test6
{
public:
    virtual void hello()
    {


    }

    //virtual void hello01() = 0;

    void printTest()
    {

    }
    void printTest01()//Multiple ordinary member functions do not occupy the memory model of class objects.
    {

    }
protected:
private:
    int a;
    static int b;
};
int main(void)
{   

    /*Initialize none of the objects in the v1 container and print them one by one during initialization*/
    MyVector<int> v1(10);

    for (int i = 0; i < v1.getLen(); i++)
    {
        v1[i] = i + 1;
        cout << v1[i] << " ";
    }
    cout << endl;

    /*Initialize none of the objects in the v2 container and print with overload after initialization*/
    MyVector<int> v2 = v1;
    for (int i = 0; i < v2.getLen(); i++)
    {
        v2[i] = i*2 + 1;
    }
    cout <<v2<< endl;


    /*Use class objects to set containers and print them*/
    Teacher t1(31, "t1"), t2(32, "t2"), t3(33, "t3"), t4(34, "t4");

    MyVector<Teacher> v3(4);

    v3[0] = t1;
    v3[1] = t2;
    v3[2] = t3;
    v3[3] = t4;

    for (int i = 0; i < v3.getLen(); i++)
    {
        Teacher tmp = v3[i];
        tmp.printTeacher();

    }

    cout<<"Hello!"<<endl;


    cout << sizeof(Test1)<<endl;
    cout << sizeof(Test2) << endl;
    cout << sizeof(Test3) << endl;
    cout << sizeof(Test4) << endl;
    cout << sizeof(Test5) << endl;
    cout << sizeof(Test6) << endl;

    Test6 t6;

    cout << sizeof(t6) << endl;
    system("pause");

    return 0;
}

Optimized Teacher class objects are stored as container elements (class template header files and implementation files remain unchanged)

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include "myvector.cpp"//Note that the cpp file is included
using namespace std;

//1. Optimize the Teacher class, change the attribute to char *panme, and allocate memory in the purchase function
//2. Optimize Teacher class, destructor releases the memory space pointed by panme
//3 Optimize Teacher class to avoid shallow copy overload = rewrite copy constructor 
//4 Optimize the Teacher class, add in the Teacher ___________. 
//5 In the template array class, store int char Teacher Teacher* (pointer type)

//=====> The concept of STL container 
class Teacher
{
public:
    Teacher()
    {
        age = 22;
        name = new char[1];
        strcpy(name, "");
    }
    Teacher(int age, char *name)
    {
        this->age = age;
        this->name = new char[strlen(name) + 1];
        strcpy(this->name,name);
    }

    Teacher(const Teacher &obj)
    {
        age = obj.age;
        name = new char[strlen(obj.name) + 1];
        strcpy(name,obj.name);
    }

    ~Teacher()
    {
        if (name != NULL)
        {
            delete[]name;
            name = NULL;
            age = 22;
        }
    }

    void printTeacher()
    {
        cout << name << "," << age << endl;
    }

    friend ostream & operator<<(ostream & out, Teacher &obj);
    Teacher & operator=(const Teacher & obj)
    {
        if (name != NULL)
        {
            delete[]name;
            name = NULL;
            age = 22;
        }

        name = new char[strlen(obj.name) + 1];

        strcpy(name, obj.name);
        age = obj.age;

        return *this;
    }
protected:
private:
    int age;
    char *name;
};


ostream & operator<<(ostream & out, Teacher &obj)
{
    out << obj.name << "," << obj.age << endl;
    return out;
}
int main(void)
{   

    /*Use class objects to set containers and print them*/
    Teacher t1(31, "t1"), t2(32, "t2"), t3(33, "t3"), t4(34, "t4");

    MyVector<Teacher> v3(4);

    v3[0] = t1;
    v3[1] = t2;
    v3[2] = t3;
    v3[3] = t4;

    for (int i = 0; i < v3.getLen(); i++)
    {
        Teacher tmp = v3[i];
        tmp.printTeacher();

    }

    cout<<"Hello!"<<endl;
    system("pause");

    return 0;
}

Pointer stored as container element

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include "myvector.cpp"//Note that the cpp file is included
using namespace std;

//1. Optimize the Teacher class, change the attribute to char *panme, and allocate memory in the purchase function
//2. Optimize Teacher class, destructor releases the memory space pointed by panme
//3 Optimize Teacher class to avoid shallow copy overload = rewrite copy constructor 
//4 Optimize the Teacher class, add in the Teacher ___________. 
//5 In the template array class, store int char Teacher Teacher* (pointer type)

//=====> The concept of STL container 
class Teacher
{
public:
    Teacher()
    {
        age = 22;
        name = new char[1];
        strcpy(name, "");
    }
    Teacher(int age, char *name)
    {
        this->age = age;
        this->name = new char[strlen(name) + 1];
        strcpy(this->name,name);
    }

    Teacher(const Teacher &obj)
    {
        age = obj.age;
        name = new char[strlen(obj.name) + 1];
        strcpy(name,obj.name);
    }

    ~Teacher()
    {
        if (name != NULL)
        {
            delete[]name;
            name = NULL;
            age = 22;
        }
    }

    void printTeacher()
    {
        cout << name << "," << age << endl;
    }

    friend ostream & operator<<(ostream & out, Teacher &obj);
    Teacher & operator=(const Teacher & obj)
    {
        if (name != NULL)
        {
            delete[]name;
            name = NULL;
            age = 22;
        }

        name = new char[strlen(obj.name) + 1];

        strcpy(name, obj.name);
        age = obj.age;

        return *this;
    }
protected:
private:
    int age;
    char *name;
};


ostream & operator<<(ostream & out, Teacher &obj)
{
    out << obj.name << "," << obj.age << endl;
    return out;
}
int main(void)
{   

    /*Use class objects to set containers and print them*/
    Teacher t1(31, "t1"), t2(32, "t2"), t3(33, "t3"), t4(34, "t4");

    MyVector<Teacher*> v3(4);

    v3[0] = &t1;
    v3[1] = &t2;
    v3[2] = &t3;
    v3[3] = &t4;

    for (int i = 0; i < v3.getLen(); i++)
    {
        Teacher *tmp = v3[i];
        tmp->printTeacher();
    }
    cout << t1;
    cout<<"Hello!"<<endl;
    system("pause");

    return 0;
}

Summary: Class template achieves the separation of data structure (specific data type) and algorithm, and truly implements generic programming.

Topics: Attribute Assembly Language Java Programming