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;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM