日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

类模板编程实验

發布時間:2024/4/18 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 类模板编程实验 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

類模板編程實驗

    • 基本知識
      • 類模板
      • 類模板的成員函數
      • 類模板的友元
      • 運算符的重載
      • 重載運算符的調用
      • 重載運算符與類的成員函數
    • 示例代碼
      • Blob.h
      • useBlob.cpp
      • 運行結果

本文是本人大一期間在校學習C++課程時所撰寫的實驗報告的摘錄,由于剛上大學,剛接觸計算機編程方面的相關知識,故可能會有很多不足甚至錯誤的地方,還請各位看官指出及糾正。
本文所涉及到的“教材”指:
電子工業出版社《C++ Primary中文版(第5版)》
如需轉載或引用請標明出處。

基本知識

類模板

類模板與函數模板類似,用于生成類的藍圖。與函數模板不同的是,編譯器不能為類模板推斷模板參數類型:為了使用模板,我們必須在模板名后面的尖括號中提供額外的信息——即用來代替模板參數的模板實參列表。

類似函數模板,類模板的定義也以關鍵字template開始,后跟模板參數列表,我們將模板參數當作替身,代替使用模板時用戶需要提供的類型或值。例如有以下類模板定義:

該類模板對于用戶指定的每一種元素類型,編譯器都生成一個不同的類:

Blob<string> names; //保存string的Blob Blob<double> prices; //保存double的Blob

這兩個定義會實例化出兩個不同的類。Names的定義創建了一個Blob類,每個T都被替換為string。prices的定義生成了另外一個Blob類,T被替換為double。

類模板的成員函數

和普通的類的定義一樣,我們既可以在類模板內部,也可以在類模板外部為其定義成員函數,且定義在類模板內的成員函數被隱式地聲明為內聯函數。

需要注意的是,在類模板外定義成員函數時,不僅要說明該成員屬于哪個類,而且,從一個模板生成的類的名字中必須包含其模板實參,且其定義要從模板參數開始。例如:

template <typename T> void Blob<T>::check(size_type i, const std::string &msg) const template <typename T> T& Blob<T>::front()

上面的兩個類模板外的函數定義都以template 開頭。

類模板的友元

如果一個類模板包含一個非模板友元,則友元被授權可以訪問所有的模板實例。如果友元自身是模板,則類可以授權給所有友元模板實例,也可以只授權給特定實例。例如:

template <typename T> class Pal; class C {friend class Pal<C>;template <typename T> friend class Pal2; };

其中只有用C實例化的Pal才是類模板C的一個友元;Pal2的所有實例都是類模板C的友元,這種情況無需前置聲明

運算符的重載

在使用類的過程中,有時我們可能會用到類似于把兩個類相關成員相加的情況,這時候雖然可以通過編寫專門的成員函數完成相加的操作,但未免太過麻煩。若可以簡單地使用運算符+即可完成操作,這無疑既簡便,又大大提高了代碼地可閱讀性。因此,可以通過運算符重載重新定義該運算符地含義,且明智地使用運算符重載能令我們的程序更易于編寫和閱讀。

重載的運算符是具有特殊名字的函數:它們的名字由關鍵字operator和其后要定義的運算符號共同組成,和其他函數一樣,重載的運算符也包含返回類型、參數列表及函數體。重載運算符函數的參數數量與該運算符作用的運算對象數量一樣多,且對于二元運算符來說,左側運算對象傳遞給第一個參數,右側運算對象傳遞給第二個參數。除了重載的函數調用運算符()之外,其他重載運算符不能含有默認實參。對于一個運算符函數來說,它或者是類的成員,或者至少含有一個類類型的參數,這一約定意味著當運算符作用于內置類型的運算對象時,我們無法改變該運算符的含義。

重載運算符的調用

通常情況下,我們將運算符作用于類型正確的實參,從而以這種簡介方式“調用”重載的運算符函數。然而,我們也能像調用普通函數一樣直接調用運算符函數,先指定函數名字,然后傳入數量正確、類型適當的實參:

data1 + data2; //普通的表達式 operator+(data1, data2); //等價的函數調用

這兩次調用是等價的,它們都調用了非成員函數operator+,傳入data1作為第一個實參、傳入data2作為第二個實參。

我們像調用其他成員函數一樣顯式地調用成員運算符函數。具體做法是,首先指定運行函數地對象(或指針)的名字,然后使用點運算符(或箭頭運算符)訪問希望調用的函數:

data1 += data2; //基于“調用”的表達式 data1. operator+=(data2); //對成員運算符函數的等價調用

這兩條語句都調用了成員函數operator+=,將this綁定到data1的地址、將data2作為實參傳入了函數。

重載運算符與類的成員函數

當我們定義重載的運算符時,必須首先決定是將其聲明為類的成員還是聲明為一個普通的非成員函數。在某些時候我們別無選擇,因為有的運算符必須作為成員;另一些情況下,運算符作為普通函數比作為成員函數更好。下面的準則有助于我們在將運算符定義為成員函數還是普通的非成員函數做出抉擇:

  • 賦值(=)、下標([])、調用(())、和成員訪問箭頭(->)運算符必須是成員
  • 復合賦值運算符一般來說應該是成員,但并非必須,這一點與賦值運算符略有不同
  • 改變對象狀態的運算符或者與給定類型密切相關的運算符,如遞增、遞減和解引用運算符,通常應該是成員
  • 具有對稱性的運算符可能轉換任意一端的運算符,例如算數、相等性、關系和位運算符等,因此它們通常應該是普通的非成員函數

此外,如果我們想提供含有類對象的混合類型表達式,則運算符必須定義成非成員函數

示例代碼

Blob.h

#ifndef BLOB_H #define BLOB_H#include <iterator> #include <string> #include <vector> #include <cstddef> #include <stdexcept> #include <utility> #include <memory> #include <algorithm> #include <iostream> #include <cstdlib> #include <stdexcept>//Blob類友元聲明必須的前置聲明 template <typename> class BlobPtr; template <typename> class Blob;//運算符==中的參數所需要的 template <typename T> bool operator==(const Blob<T>&, const Blob<T>&);template <typename T> class Blob {//每個Blob實例將訪問權限授予用相同類型實例化的BlobStr和相等運算符== friend class BlobPtr<T>;friend bool operator==<T> (const Blob<T>&, const Blob<T>&); public:typedef T value_type; //value_type為T的別名typedef typename std::vector<T>::size_type size_type; //簡寫Blob(); //默認構造函數的聲明template <typename It> Blob(It b, It e); //接受兩個It類型(指針)b、e作為參數的構造函數的聲明Blob(T*, std::size_t); //接受一個T類型的指針以及一個size_t作為形參的構造函數的聲明BlobPtr<T> begin() { return BlobPtr<T>(*this); } //返回指向第一個T類型元素的BlobPtrBlobPtr<T> end() //返回指向尾后T類型元素的BlobPtr{BlobPtr<T> ret = BlobPtr<T>(*this, data->size()); return ret;}size_type size() const { return data->size(); } //返回該Blob中元素的數量bool empty() const { return data->empty(); } //判斷該Blob是否為空void push_back(const T &t) {data->push_back(t);} //在該Blob中添加元素 void pop_back(); //在該Blob中刪除元素T& front(); //返回該Blob中的第一個元素T& back(); //返回該Blob中的最后一個元素T& at(size_type); //返回該Blob中對應下標的元素const T& back() const; //const版本的bcakconst T& front() const; //const版本的frontconst T& at(size_type) const; //const版本的atT& operator[](size_type i); //重載下標運算符const T& operator[](size_type i) const; //const版本的重載下標運算符void swap(Blob &b) { data.swap(b.data); } //將該Blob中的數據(指針指向)與Blob類b交換 private:std::shared_ptr<std::vector<T>> data; //data為指向存儲類型T的vector的智能共享指針void check(size_type i, const std::string &msg) const; //當i的位置不合法時拋出異常 };//接受一個T類型的指針p以及一個size_tn作為形參的構造函數,用從p指向的位置開始往后n個位置的T類型元素初始化Blob中的數據 template <typename T> Blob<T>::Blob(T *p, std::size_t n) : data(new std::vector<T>(p, p + n)) { } template <typename T> Blob<T>::Blob() : data(new std::vector<T>()) { } //默認構造一個空的vector //接受兩個It類型(指針)b、e作為參數的構造函數,用b、e間的T類型元素初始化Blob中的數據 template <typename T> template <typename It> Blob<T>::Blob(It b, It e) : data(new std::vector<T>(b, e)) { }template <typename T> void Blob<T>::check(size_type i, const std::string &msg) const //檢查函數 {if (i >= data->size())throw std::out_of_range(msg); }template <typename T> T& Blob<T>::front() {check(0, "front on empty Blob"); //先檢查該Blob是否為空return data->front(); }template <typename T> T& Blob<T>::back() {check(0, "back on empty Blob"); //先檢查該Blob是否為空return data->back(); }template <typename T> void Blob<T>::pop_back() {check(0, "pop_back on empty Blob"); //先檢查該Blob是否為空data->pop_back(); }template <typename T> const T& Blob<T>::front() const {check(0, "front on empty Blob"); //先檢查該Blob是否為空return data->front(); }template <typename T> const T& Blob<T>::back() const {check(0, "back on empty Blob"); //先檢查該Blob是否為空return data->back(); }template <typename T> T& Blob<T>::at(size_type i) {check(i, "subscript out of range"); //先檢查i的值是否合法,防止訪問一個不存在的元素return (*data)[i]; //(*data)是data指向的vector }template <typename T> const T& Blob<T>::at(size_type i) const {check(i, "subscript out of range"); //先檢查i的值是否合法,防止訪問一個不存在的元素return (*data)[i]; //(*data)是data指向的vector }template <typename T> T& Blob<T>::operator[](size_type i) {check(i, "subscript out of range"); //先檢查i的值是否合法,防止訪問一個不存在的元素return (*data)[i]; }template <typename T> const T& Blob<T>::operator[](size_type i) const {check(i, "subscript out of range"); //先檢查i的值是否合法,防止訪問一個不存在的元素return (*data)[i]; }//重載運算符<<,格式化輸出一個Blob template <typename T> std::ostream& operator<<(std::ostream &os, const Blob<T> a) {os << "< ";for (size_t i = 0; i < a.size(); ++i) os << a[i] << " ";os << " >";return os; }//重載運算符==,判斷兩個Blob是否相等 template <typename T> bool operator==(const Blob<T> lhs, const Blob<T> rhs) {if (rhs.size() != lhs.size()) //先判斷元素個數是否相等return false;for (size_t i = 0; i < lhs.size(); ++i) //再判斷每個元素是否對應相等(有順序要求){if (lhs[i] != rhs[i])return false;}return true; } //BlobPtr類友元聲明必須的前置聲明:接受兩個BlobPtr的常量引用為參數的重載運算符== template <typename T> bool operator==(const BlobPtr<T>&, const BlobPtr<T>&);//當試圖訪問不存在的元素時BlobPtr拋出異常 template <typename T> class BlobPtr : public std::iterator<std::bidirectional_iterator_tag,T> {//每個BlobPtr實例將訪問權限授予用相同類型實例化的相等運算符==friend bool operator==<T>(const BlobPtr<T>&, const BlobPtr<T>&); public:BlobPtr(): curr(0) { } //默認構造函數:初始位置為0//接受一個Blob的引用以及可選的size_t類型sz作為參數的構造函數:成員wptr指向該Blob的數據,以sz作為初始位置BlobPtr(Blob<T> &a, size_t sz = 0) : wptr(a.data), curr(sz) { }T &operator[](std::size_t i) //重載下標運算符{std::shared_ptr<std::vector<T>> p = check(i, "subscript out of range"); //先檢查i的值是否合法return (*p)[i]; //(*p)是一個存儲T類型元素的vector}const T &operator[](std::size_t i) const //重載const版本的下標運算符 {std::shared_ptr<std::vector<T>> p = check(i, "subscript out of range"); return (*p)[i]; //(*p)是一個存儲T類型元素的vector}T& operator*() const //重載取地址運算符*{std::shared_ptr<std::vector<T>> p = check(curr, "dereference past end");//先檢查當前地址是否合法return (*p)[curr]; //(*p)是一個存儲T類型元素的vector}T* operator->() const //重載成員訪問運算符->{return & this->operator*(); //起作用的實際上是重載的取地址運算符*}BlobPtr& operator++(); //重載前綴形式的遞增運算符++的聲明BlobPtr& operator--(); //重載前綴形式的遞減運算符--的聲明BlobPtr operator++(int); //重載后綴形式的遞增運算符++的聲明BlobPtr operator--(int); //重載后綴形式的遞減運算符--的聲明private://檢查函數:如果檢查通過,則返回一個指向對應vector的shared_ptrstd::shared_ptr<std::vector<T>> check(std::size_t, const std::string&) const;std::weak_ptr<std::vector<T>> wptr; //使用弱指針weak_ptr,意味著其指向的vector可能會被銷毀,方便檢查std::size_t curr; //用于指示位置 };template <typename T> bool operator==(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs) //重載相等運算符== {//當兩個參數指向的位置相等且它們各自在vector中的位置相等時,返回1return lhs.wptr.lock().get() == rhs.wptr.lock().get() && lhs.curr == rhs.curr; }template <typename T> bool operator!=(const BlobPtr<T> &lhs, const BlobPtr<T> &rhs) //重載不等運算符!= {return !(lhs == rhs); //起作用的實際上是重載的相等運算符== }template <typename T> std::shared_ptr<std::vector<T>> BlobPtr<T>::check(std::size_t i, const std::string &msg) const {std::shared_ptr<std::vector<T>> ret = wptr.lock(); //檢查wptr指向的vector是否存在 if (!ret)throw std::runtime_error("unbound BlobPtr"); //若不存在,則拋出異常if (i >= ret->size())throw std::out_of_range(msg); //若i的值不合理,則拋出異常return ret; //若通過檢查,則返回一個指向該vector的shared_ptr }//后綴形式:先返回值,再遞增/遞減 template <typename T> BlobPtr<T> BlobPtr<T>::operator++(int) {//此處無需檢查,對前綴形式的遞增運算符++的調用會執行檢查BlobPtr ret = *this; //保存當前的BlobPtr++*this; //前綴形式的遞增運算符++執行檢查,通過則位置加1return ret; //返回臨時保存的BlobPtr }template <typename T> BlobPtr<T> BlobPtr<T>::operator--(int) {//此處無需檢查,對前綴形式的遞減運算符--的調用會執行檢查BlobPtr ret = *this; //保存當前的BlobPtr--*this; //前綴形式的遞減運算符--執行檢查,通過則位置加1 return ret; //返回臨時保存的BlobPtr }//前綴形式:先遞增/遞減,再返回值 template <typename T> BlobPtr<T>& BlobPtr<T>::operator++() {check(curr, "increment past end of BlobPtr"); //檢查:如果curr指示的位置已是尾后元素,則不能遞增,拋出異常++curr; //位置加1return *this; }template <typename T> BlobPtr<T>& BlobPtr<T>::operator--() {//如果curr指示的是第一個元素,則不能遞減--curr; //位置減1check(-1, "decrement past begin of BlobPtr"); //檢查:如果curr指示的位置在首元素之前,則拋出異常return *this; } #endif

useBlob.cpp

#include <string> #include <iostream>using namespace std;#include "Blob.h"int main() {Blob<string> b1; //默認構造一個空的Blob為b1cout << b1.size() << endl; //驗證b1內的元素個數為0{ //一個新的作用域string temp[] = {"a", "an", "the"}; //temp為臨時數組,存儲幾個字符串//用Blob其中的一個構造函數,初始化b2,它的元素就是temp中的元素Blob<string> b2(temp, temp + sizeof(temp)/sizeof(*temp));b1 = b2; //b1和b2指向相同的vectorb2.push_back("about"); //向b2指向的vector中加入新元素cout << b1.size() << " " << b2.size() << endl; //通過比較b1和b2指向的vector的元素個數,驗證它們指向的是同一個vector} //b2的作用域結束,b2被銷毀,但由于仍有一個b1中的shared_ptr指向該vector,所以該vector并未被銷毀cout << b1.size() << endl; //輸出此時b1內的元素個數 for(BlobPtr<string> p = b1.begin(); p != b1.end(); ++p) //使用各種重載后的運算符實現遍歷操作cout << *p << endl;//----------下面是新增的調試內容----------//int temp[] = { 0,1,2,3,4,5,6,7,8,9 };Blob<int> b3(temp, temp + sizeof(temp) / sizeof(*temp));putchar('\n');cout << b3.front() << " " << b3.back() << endl; //調試front和back函數cout << b3.at(2) << endl; //調試at函數cout << b3[2] << endl; //調試重載的下標運算符[]b3.pop_back(); //調試pop_back函數cout << b3 << endl; //調試重載后的運算符<<system("pause");return 0; }

運行結果

總結

以上是生活随笔為你收集整理的类模板编程实验的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。