CH15 Templates, Vector and Exception
本文為 2021-Fall 學期旁聽台大資管系孔令傑教授開授的 Programming Design 所記錄的課程筆記。課程內容程式碼可以參閱我的 Github repo: C++ Programming-Design-2021-Fall
Templates
Recall Warriors and wizards
class Character
{
protected:
static const int EXP_LV = 100;
string name;
int level;
int exp;
int power;
int knowledge;
int luck; // ...
};
在前一章節的範例中我們用 srtring name
作為key來搜尋或區別不同的object,但當有多個object的name attribute是相同的時候就會出問題,因此可能會想要把string name這個attribute改成 int id之類的來做為區別不同object的key。然而若我們把name的type改為int,或許我們某一天又會想要把它改回來成string,我們可能會考慮implement兩個不同版本的class,但這又路造成閱讀與維護上的麻煩。
Templates
從上面Character的範例中我們需要key
的data type
可供彈性選擇,在C++裡templates
就可以做到這件事,且不只可以應用在class也可以應用在function。
C++ class templates 要求我們在呼叫function或建立物件時傳入一個data-type argument
,如:
Warrior<string> w1("Alice", 10);
Wizzard<int> w2(16, 5);
Template declaration
Template的用意就是讓使用者可以一情況決定傳入或者要使用的member的data type
,因此data type對於template來說就像是一個變數,我們在宣告的時候會用到template
與typename
template<typename T>
class TheClassName
{
// T can be treated as a type inside the class definition block
};
當使用template來宣告class,它的成員變數也要一起改變寫法
template<typename T>
T TheClassName<T>::f(T t)
{
// t is a variable whose type is T
};
template<typename T>
void TheClassName<T>::f(int i)
{
//follow the rule even if T is not used
};
Template invocation
To instantiate an object, pass a type argument.
int main()
{
TheClassName<int> a;
TheClassName<double> b;
TheClassName<AnotherClassName> c;
};
exp:
#include <iostream>
using namespace std;
template<typename T>
void f(T t)
{
cout << t;
}
int main()
{
f<double>(1.2); // 1.2
f<int>(1.2); // 1
return 0;
}
// When we invoke f with f<double>, the function is
void f(double t)
{
cout << t;
}
// When we invoke f with f<int>, the function is
void f(int t)
{
cout << t;
}
We may also have multiple type parameters.
#include <iostream>
using namespace std;
template<typename A, typename B>
void g(A a, B b)
{
cout << a + b << endl;
}
int main()
{
g<double, int>(1.2, 1.7); // 2.2
return 0;
}
An example with classes
#include <iostream>
using namespace std;
template<typename T>
class C
{
public:
T f(T i);
};
template<typename T>
T C<T>::f(T i)
{
return i * 2;
}
int main()
{
C<int> c;
cout << c.f(10) << endl;
return 0;
}
Revising the classes
Let’s revise our definitions of Character, Warrior, Wizard, and Team.
template <typename KeyType>
class Character
{
protected:
static const int EXP_LV = 100;
KeyType name;
int level;
int exp;
int power;
int knowledge;
int luck;
void levelUp(int pInc, int kInc, int lInc);
public:
Character(KeyType n, int lv, int po, int kn, int lu);
virtual void beatMonster(int exp) = 0;
virtual void print();
KeyType getName();
};
參考15_1.cpp
當parent class使用template後,child繼承時就不能再使用原本的parent's class name了,必須要加上<typename>
template <typename KeyType>
class Warrior : public Character<KeyType> // no class "Character"
{ // there is "Character<int>",
private: // "Character<string>", etc.
static const int PO_LV = 10;
static const int KN_LV = 5;
static const int LU_LV = 5;
public:
Warrior(KeyType n, int lv = 0);
void print();
void beatMonster(int exp);
};
In the main function
使用template以後Team就可以用string作為key type也可以用int作為key type,有更靈活的運用
int main()
{
Team<string> t;
t.addWarrior("Alice", 1);
t.memberBeatMonster("Alice", 10000);
t.addWizard("Bob", 2);
t.printMember("Alice");
Team<int> t2;
t2.addWarrior(1, 1);
t2.memberBeatMonster(1, 10000);
t2.addWizard(2, 2);
t2.printMember(1);
return 0;
}
Remark
當我們做了class template,很可能會預到自己傳入的data type不是basic data type,而不能直接做opration的狀況(像是傳入一個class進去),那這時我們就得為有可能傳入的KeyType做operation overloading
template <typename KeyType>
void Team<KeyType>::memberBeatMonster(KeyType name, int exp) {
for(int i = 0; i < this->memberCount; i++) {
if(this->member[i]->getName() == name) {
this->member[i]->beatMonster(exp);
break; }
}
}
The standard library <vector>
A good reason to use templates
For strings:
- We use a character array to represent a C string.
- We use the class string to represent a C++ string.
- The latter is to
embed the former into a class
andadd useful functions
.
除了char,我們也會想對int, double等data type做一樣的事,將資料包進class中的動態陣列並且添加一些有用的function。
All we need is a class with an embedded dynamic array
for something
.
Perfect fortemplates
!
The standard library <vector>
In C++, there is a standard template library (STL)
.
- It provides containers, iterators, algorithms, and functions.
The class vector
with templates is defined and implemented in the standard
library <vector>
. It is just a “dynamic vector” of any type.
- It is a class with an embedded one-dimensional dynamic array.
- It has many useful member functions (including overloaded operators).
- It is implemented with templates.
create a vector:
vector<int> v1; // integer vector
vector<double> v2; // double vector
vector<Warrior> v3; // Warrior vector
- Member functions that modifies a vector: push_back(), pop_back(), insert(), erase(), swap(), =, etc.
- Member functions for one to access a vector element: [], front(), back(), etc.
- Member functions related to the capacity: size(), max_size(), resize(), etc.
Rewriting Team using vector
template <typename KeyType>
class Team
{
private:
vector<Character<KeyType>*> member; // Character<KeyType>*的vector
public:
Team();
~Team();
void addWarrior(KeyType name, int lv);
void addWizard(KeyType name, int lv);
void memberBeatMonster(KeyType name, int exp);
void printMember(KeyType name);
};
template <typename KeyType>
void Team<KeyType>::addWarrior(KeyType name, int lv)
{
Warrior<KeyType>* wPtr = new Warrior<KeyType>(name, lv); // 必須用動態記憶體配置,因為local variable會在函數呼叫結束自動被清除
this->member.push_back(wPtr);
}
template <typename KeyType>
void Team<KeyType>::addWizard(KeyType name, int lv)
{
Wizard<KeyType>* wPtr = new Wizard<KeyType>(name, lv);
this->member.push_back(wPtr);
}