C++中模板的使用

为什么使用模板:

  C++重载(overloading),重载函数一般含有不同的参数个数或不同的参数类型。如果使用MAX函数对不同类型的求最大值,就要定义不同的重载函数:

  

int max(int x,int y)
{
    if(x>y)
        return x;
    else 
        return y;
}

float max(float x,float y)
{
    if(x>y)
        return x;
    else 
        return y;
}

但是如果要用double型,又需要重新定义。。。

模板就是为了解决重载函数定义不全的问题,它可以实现类型参数化,即把类型定义为参数,从而实现代码的可重用性。模板分为两类:函数模板和类模板

函数模板:

Template<class 或 typename T>

template是一个声明的模板的关键字,表示声明一个模板关键字class不能省。

template <class T>
T min(T x,T y)

{
    return(x<y)?x:y;
}

类模板定义:

template <class T>

class 类名{

};

template <class T1,class T2>
class student{
    T1 a;
    T2 b;
public:
    student(T1 a,T2 b);//constructor
    void show();
};

template<class T1, class T2>
student<T1,T2>::student(T1 a,T2 b):a(a),b(b){}
template<class T1,class T2>
void student<T1,T2>::show()
{
    cout<<a<<b<<endl;
}
int main()
{
    student<char ,char> s('a','b');
    s.show();
    return 0;
}

把类的属性定义为private,保护数据,不能外部随意访问,但是可以定义一个访问内部数据的函数接口。也可以定义一个修改内部属性的函数(那这与外部访问数据和修改数据有什么区别,这样可以在修改函数里加各种限制。哈哈)

array < int > a(10);

array < int >表明用int类型来代替模板类中的类参数“T”,编译器会将模板类array中所有的类参数T都用int来代替。例如类中的私有成员变量“T * num;”会被替换为“int * num;”。对类中的成员函数也会进行相同的替换,如“T & operator[]( int );”将会被替换为“int & operator[]( int );”。

using namespace std;
template< class T >
class array
{
public:
    array( int );
    T & operator[]( int );//操作符[]取第int个数,返回类型为T。。
    const T & operator[] ( int )const;
    int getlen() const { return length; }
    ~array();
private:
    array(){};
    int length;
    T * num;
};
template < class T >
array< T >::array( int n )
{
    num = new T[n];
    length = n;
}
template < class T >
array< T >::~array()
{
    delete[] num;
}
template< class T >
T & array< T > ::operator[] ( int i )
{
    if( i < 0 || i >= length )
        throw string( "out of bounds" );
    return num[i];
}
template< class T >
const T & array< T > ::operator[] (int i) const
{
    if( i < 0 || i >= length)
        throw string( "out of bounds" );
    return num[i];
}
template< class T >
ostream & operator<<( ostream & out, const array <T> & A )
{
    for(int i=0; i < A.getlen(); i++)
        out<< A[i] << " ";
    return out;
}
int main()
{
    array< int > A(10);
    for(int i = 0; i < 10; i++)
    {
        A[i] = 2*i;       
    }
    cout<<A<<endl;
    return 0;
}

C++编程技巧:对象与实例的区别,new与不用new的区别:

  class A a; a 在栈里;

  class A a=new A; A 在堆里;

  new创建类对象需要指针接收,一处初始化,多处使用;     CTest* p= new CTest();//new申请的对象,则只有调用到delete时再会执行析构函数 ; 不用new   CTest mTest;//不需要手动释放,该类析构函数会自动执行

  new需要delete销毁

  new创建对象在堆空间

  频繁调用不适合用new,要申请和释放内存

用new 生成对象,上面的例子写为:

int main()
{
    student<char ,char>* s=new student<char ,char >('a','b');
    s->show();
    delete(s);
    return 0;
}

(java中不同:JAVA:  

  A a = new A();  

  为A对象创建了一个实例,但在内存中开辟了两块空间:一块空间在堆区,存放new A()这个对象;  

  另一块空间在堆栈,也就是栈,存放a,a的值为new A()这个对象的内存地址。因为java在JVM中运行,  

  所以a 描述的内存地址不一定是这个对象真实内存的地址。 

  Object o; // 这是声明一个引用,它的类型是Object,他的值为null,还没有指向任何对象,该引用放在内存的栈区域

  o = new Object(); // new Object()句,实例化了一个对象,就是在堆中申请了一块连续空间用来存放该对象。 

  = // 运算符,将引向o指向了对象。也就是说将栈中表示引用o的内存地址的内容改写成了Object对象在堆中的地址。

  

  创建对象:student stu1 = new  student();

  通常把这条语句的动作称之为创建一个对象,其实,它包含了四个动作。

  1)右边的"new  student",是以student类为模板,在堆空间里创建一个student类的对象(也简称为student对象)。

  2)末尾的()意味着,在对象创建后,立即调用student类的构造函数,对刚生成的对象进行初始化。

      构造函数是肯定有的。如果你没写,Java会给你补上一个默认的构造函数。

  3)左边的"student stu1"创建了一个student类引用变量。所谓student类引用,就是以后可以用来指向某个 

      student对象的对象引用,它指向的是某个student对象的内存地址(有点C语言中指针的味道)。

  4)"="操作符使对象引用指向刚创建的那个student对象。

      我们可以把这条语句拆成两部分:student  stu1;                 (1)

                                                             stu1 = new student();     (2)

        效果是一样的。

  C++ 如果直接定义类,如classA a; a 存在栈上(也意味着复制了对象a在栈中),如果classA  a = new classA就存在堆

非类型模板参数:

C++中:

Student  student(20) ;  //这里student是引用 对象分配在 栈空间中,这里只是我的理解

Student *student = new  Student(20);  //这里student是指针,new Student(20)是分配在堆内存空间的

  1. Student stu;//对象 stu 在栈上分配内存,需要使用&获取它的地址
  2. Student *pStu = &stu;//创建对象指针

       在堆上创建对象,这个时候就需要使用前面讲到的new关键字

  1. Student *pStu = new Student;

在栈上创建出来的对象都有一个名字,比如 stu,使用指针指向它不是必须的。但是通过 new 创建出来的对象就不一样了,它在堆上分配内存,没有名字,只能得到一个指向它的指针,所以必须使用一个指针变量来接收这个指针,否则以后再也无法找到这个对象了,更没有办法使用它。也就是说,使用 new 在堆上创建出来的对象是匿名的,没法直接使用,必须要用一个指针指向它,再借助指针来访问它的成员变量或成员函数。栈内存是程序自动管理的,不能使用 delete 删除在栈上创建的对象;堆内存由程序员管理,对象使用完毕后可以通过 delete 删除。在实际开发中,new 和 delete 往往成对出现,以保证及时删除不再使用的对象,防止无用内存堆积。

new A以及new A()的区别:

调用new分配的内存有时候会被初始化,而有时候不会,这依赖于A的类型是否是POD(Plain old data)类型,或者它是否是包含POD成员、使用编译器生成默认构造函数的类。POD class没有用户定义的析构函数、拷贝构造函数和非静态的非POD类型的数据成员。没有用户定义的构造函数,没有私有的或者保护的非静态数据,没有基类或虚函数。它只是一些字段值的集合,没有使用任何封装以及多态特性。

#include <iostream>  
  
using namespace std;  
  
struct A { int m; }; // POD  
struct B { ~B(){}; int m; }; // non-POD, compiler generated default ctor  
struct C { C() : m() {}; ~C(){}; int m; }; // non-POD, default-initialising m  
  
int main()  
{  
    A *aObj1 = new A;  
    A *aObj2 = new A();  
    cout << aObj1->m << endl;  
    cout << aObj2->m << endl;  
  
    B *bObj1 = new B;  
    B *bObj2 = new B();  
    cout << bObj1->m << endl;  
    cout << bObj2->m << endl;  
  
    C *cObj1 = new C;  
    C *cObj2 = new C();  
    cout << cObj1->m << endl;  
    cout << cObj2->m << endl;  
  
    delete aObj1;  
    delete aObj2;  
    delete bObj1;  
    delete bObj2;  
    delete cObj1;  
    delete cObj2;  
  
    return 0;  
}

new A:不确定的值

new A():value-initialize A,由于是POD类型所以是zero initialization

new B:默认构造(B::m未被初始化)

new B():value-initialize B,zero-initialize所有字段,因为使用的默认构造函数

new C:default-initialize C,调用默认构造函数

new C():value-initialize C,调用默认构造函数

在所有C++版本中,只有当A是POD类型的时候,new A和new A()才会有区别。而且,C++98和C++03会有区别。

但是在Java中

Student  student(20) ;  //注意:java中没有这样实例化对象的, 要想得到一个对象 必须要new出来.

Student student ; //这个只是定义了一个引用 ,没有指向任何对象

Student student = new Student(20);   //定义了一个引用,指向堆内存中的student对象

常数,或指向外部链接对象的指针。

template <class T,MAXSIZE>
class Stack{
    T elemsp[MAXSIZE];
};
Stack<int, 20> intStack;
Stack<int ,10> intStack2;
原文地址:https://www.cnblogs.com/fanhaha/p/7055345.html