class Fraction
{
public:
explicit Fraction(int num, int den=1)
: m_numerator(num), m_denominator(den)
{ cout << m_numerator << ' ' << m_denominator << endl; }
......
operator double() const {
return (double)m_numerator / m_denominator;
}
......
private:
int m_numerator; //
int m_denominator; //
};
值得注意的一點是,所有的轉換函數都不需要聲明返回參數類型,也不需要輸入參數。可以通過(double) obj_of_Fraction 進行調用。
除了顯式使用轉換函數以外,轉換函數也會進行隱式調用,例如:
Fraction f(3,5);
double d = 4 + f;
在這種情況下,首先,編譯器會檢查是否存在+運算符的合適的函數重載。如果沒有合適的函數重載被定義,則會先將f隱式轉換為double,再進行相加。
Fraction operator+(const Fraction& f) {
cout << "operator+(): " << f.m_numerator << ' ' << f.m_denominator << endl;
//... plus
return f;
}
那麼,對於d2 = f + 4;有以下兩種路徑,產生了二義性從而不能通過。
為了消除這一二義性,在構造函數前面加了explicit關鍵字。Explicit關鍵字的意義是,這個函數只可以被顯示調用,而不能被任何形式地隱式調用。 (由於這種聲明,Fraction a = 1也不能被調用了。)
如果給拷貝構造函數加explicit關鍵字,則Fraction B(A) 可以使用,Fraction B = A也不能使用了。
template <class T>
class shared_ptr{
public :
T& operator* () const { return *px; }
//*運算符作為成員函數默認的重載方式為操作符在前,注意返回值為引用、
T* operator->() const { return px; }
//注意返回值為指針類型
shared_ptr( T *p ):px(p){}
//以上三個函數是幾乎所有智能指針都需要的。
private:
T* px;
......
}
這裡需要特別說明一下對於->操作符重載的調用,考慮如下代碼:
shared_ptr<Foo> sp(new Foo);
Foo f(*sp);
sp->method();
將會自動轉化為px->method();這裡涉及到了'->'的特別行為。'->'運算符的一個特點是,它不會再運算過程中被消耗。'sp->'的運算結果是px,又由於'->'沒有被消掉,因此其轉換為px->method()。
其和智能指針基本一致,只是還需要對++和—運算符進行重載。
T& operator *() const{ return (*node).data; }
T* operator->() const{ return &(operator*();)}
需要注意的一點是,這裡顯式調用了operator*(),這種形式是非常方便的。
即重載()運算符(又名函數調用操作符,其重載時的默認調用位置就在中間),在標准庫中被廣泛使用。
template <class pair>
struct select1st: public uniary_func<.......>{
const typename pair::first_type&
operator()(const pair &x) const{
return x.first;
}
}
這樣就能夠像函數一樣使用這個類,select1st(pair(1,2));
泛型編程和面向對象編程是C++的兩個重要方面。
通常來講,類模板可以被用來做容器、迭代器等;而函數模板則是被用來做泛型算法。
只需要使用template <class/typename T>進行聲明。
類模板在使用時需要指明參數類型,但函數模板可以自動進行實參推導。
template <class T1, class T2>
struct pair {
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair() : first(T1()), second(T2()) {}
pair(const T1& a, const T2& b) : first(a), second(b) {}
template <class U1, class U2>
pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
};
注意其中的成員函數——拷貝構造函數。通過這種成員模板方式,可以實現拷貝構造。
模板是一種泛化操作,而特化則是一種逆向的操作,其語法是:
template<>
struct hash<int>{
size_t operator() (int x) const {return x;}
}
通過這種形式,可以給int參數類型單獨制定方法,同樣的語法也可以使用與函數模板。
cout<<hash<int>()(1000);
其中的hash<int>() 是聲明了一個無參臨時對象。
與全特化類似,這種偏特化也是固定參數進行特化。
泛化聲明:
template<class T1,class T2>
class vector
{......}
偏特化聲明:
template<class T2>
class vector <bool,T2>
{......}
注意偏特化聲明中的T2雖然仍寫為T2,但與全泛化的T2並沒有關系。
這種偏特化是指對於 const * p類型、*p類型、p類型的限制,偏特化就是按照范圍劃分不同的方法,例如如下的代碼:
template <class T>
class C{......}
template <class T>
class C<T*>{......}
當僅有第一種定義時,會自動調用第一種類型的模板;然而當有上兩種定義時,對於指針類型的模板參數,可以使用特別的方法定義,這種分類是十分必要的。
template<typename T,
template <typename T>
class Container
>
class XCls
{
private:
Container<T> c;
public:
XCls()
{
for(long i=0; i< 100; ++i)
c.insert(c.end(), T());
}
};
注意第二個模板參數template <typename T> class SmartPtr ,其本身又是一個模板。
使用方法:
template <class T>
using Lst = list<T, allocator<T>>;
XCls<string, Lst> mylist;
注意:下面的用法不是模板模板參數。
template< class T ,class Sequence = deque<T>>
class stack{
protected:
Sequence C;
}
其中第二個參數制定了stack要依靠什麼基本容器類型進行重載,其具有默認值deque。
使用方式為: stack<int,list<int>> s2; 這裡的list已經不是模板了,它在具體化之前已經變成了普通的模板參數。
void print(){}
template< typename T , typename... Types >
void print(const T& firstArg,const Types &... args){
cout<<firstArg<<endl;
print(args...);
}
…是C++11中的新類型,…代表包(package),也就是以容器存儲的數量不定的參數。對於這種數量不定的模板參數,始終采取將參數分為一個和一包的辦法,這個包可以作為模板參數,也可以作為函數參數。在print()中,其采用了遞歸調用方法。也可以將第二參數其作為容器使用。
變量會在容器中遍歷,直到變量結尾。
e.g.
for(auto i:{1,2,3,4}){
......
}
這種語法搭配引用可以修改容器中的值。
for(auto &elem:vector_obj){
elem *= 3;
}