課堂大綱
1.Conversion Function 轉換函數
2.non-explicit-one-argument ctor 可隱式轉換單一形參構造函數
3.explicit-one-argument ctor 非隱式轉換單一形參構造函數
4.兩種特殊的類
4.1. pointer-like class 智能指針
4.2. pointer_like class 迭代器
4.3. function-like classes: functor 仿函數
5.模板
5.1. 類模板
5.2. 函數模板
5.3. 成員模板
5.4. 模板特化
5.5.模板模板參數
6.關于C++標準庫
7.reference 引用
注:由于本人對這部分內容的了解不深,所以無法寫出很詳細的心得,更多的是資料的匯總,引用是從網絡上摘取出來的,請見諒。
正文
1.Conversion Function 轉換函數
形式如:
#include<iostream>
using namespace std;
class Fraction
{
public:
Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) { }
operator double( ) const
{
cout<<"operator double( ) is called!!!"<<endl;
return (double)(m_numerator / m_denominator);
}
private:
int m_numerator; //分子
int m_denominator; //分母
};
int main( )
{
Fraction f(3, 5);
double d = 4+f; //調用operator double( ) 將f 轉換為0.6.
return 0;
}
運行結果:
轉換函數的基本規則:
-轉換函數只能是成員函數,無返回值,空參數。
-不能定義到void的轉換,也不允許轉換成數組或者函數類型。
-轉換常定義為const形式,原因是它并不改變數據成員的值。
來源
在設計類的時候,如果覺得有必要寫轉換函數以及認為合理的,都是可以寫成轉換函數,
用于將該類型轉換成其他類型的。
本類型->其他類型
2.non-explicit-one-argument ctor 可隱式轉換單一形參構造函數
形式如:
#include<iostream>
using namespace std;
class Fraction
{
public:
Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den)
{
cout<<"non-explicit ctor is called!!!"<<endl;
}
Fraction operator + (const Fraction &f)
{
cout<<"operator +(...) is called!!!"<<endl;
return Fraction(this->m_numerator+f.m_numerator, this->m_denominator+f.m_denominator);
}
int getNumerator() const {return m_numerator;}
int getDenominator() const {return m_denominator;}
private:
int m_numerator; //分子
int m_denominator; //分母
};
ostream& operator<<(ostream &os, const Fraction &f)//重載<<運算符
{
return os<<"分子: "<<f.getNumerator()<<"分母: "<<f.getDenominator();
}
int main( )
{
Fraction f(3, 5); //調用non-explicit ctor
Fraction d2 = f+4; //調用non-explicit ctor將4轉為 Fraction(4,1)
//然后調用operator +
cout<<d2<<endl; //因為引用傳遞,所以會再調用一次non-explicit ctor
return 0;
}
運行結果:
那么如果轉換函數和運算符+重載都在同一個類里面呢?按照上述的程序,會發生什么問題?
class Fraction
{
public:
...
Fraction operator + (const Fraction &f)
{
cout<<"operator +(...) is called!!!"<<endl;
return Fraction(this->m_numerator+f.m_numerator, this->m_denominator+f.m_denominator);
}
operator double( ) const
{
cout<<"operator double( ) is called!!!"<<endl;
return (double)(m_numerator / m_denominator);
}
...
};
編譯結果:
我個人認為是編譯器不知道需要調用內置+運算還是用戶自定義的+運算
緣由在于:
1.如果編譯器利用類中的double()來將f轉換為double型后,便可以用內置+號來作運算;
2.但編譯器也可以將4轉換成Fraction,然后在調用用戶自定義的operator+。
但編譯器不知道用戶的真實目的是如何,所以便報錯了。
有些時候編譯器幫助我們完成的隱式轉換會帶來錯誤,為了解決這個問題,我們為此引入一個關鍵字↓
3.explicit-one-argument ctor 非隱式轉換單一形參構造函數
class Fraction
{
public:
explicit Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) //請注意前面的關鍵字
{
cout<<"non-explicit ctor is called!!!"<<endl;
}
Fraction operator + (const Fraction &f)
{
cout<<"operator +(...) is called!!!"<<endl;
return Fraction(this->m_numerator+f.m_numerator, this->m_denominator+f.m_denominator);
}
operator double( ) const
{
cout<<"operator double( ) is called!!!"<<endl;
return (double)(m_numerator / m_denominator);
}
編譯結果是:
因為加了explicit關鍵字后,構造函數不會發生隱式轉換,所以double轉向Fraction時就會報錯。
explicit 只對構造函數起作用,用來抑制隱式轉換。
來源
4.兩種特殊的類
4.1. pointer-like class 關于智能指針
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;
long* pn;
};
使用舉例:
struct Foo
{
......
void method(void) {......}
};
shared_ptr<Foo> sp(new Foo);
Foo f(*sp); //*sp則調用shared_ptr類中的*重載運算符,返回了Foo類的一個指針所指的【對象】;
sp->method( ); //調用shared_ptr類中的->重載運算符,返回了一個Foo的指針。
//->比較特別,解引用之后還能繼續傳遞下去。
網上摘取一段關于shared_ptr的使用例子
**shared_ptr<int> sp(new int(10)); //一個指向整數的shared_ptr
assert(sp.unique()); //現在shared_ptr是指針的唯一持有者
shared_ptr<int> sp2 = sp; //第二個shared_ptr,拷貝構造函數
assert(sp == sp2 && sp.use_count() == 2); //兩個shared_ptr相等,指向同一個對象,引用計數為2
sp2 = 100; //使用解引用操作符修改被指對象
assert(sp == 100); //另一個shared_ptr也同時被修改
sp.reset(); //停止shared_ptr的使用
assert(!sp); //sp不再持有任何指針(空指針)
**
[來源](http://blog.csdn.net/sndaxdrs/article/details/6175701)
*注意:assert的作用是現計算表達式 expression ,如果其值為假(即為0),那么它先向stderr打印一條出錯信息,然后通過調用 abort 來終止程序運行*
個人覺得,智能指針實際上就是傳統普通指針的擴展,所以究其本質,其內部還是必須有一個普通指針,然后再拓展其功能。
####4.2. pointer_like class 迭代器
示例程序:
```C++
template<class T>
class __list_node
{
public:
void* prev;
void* next;
T data;
};
template<class T, class Ref, class Ptr>
class __list_iterator
{
public:
...
typedef Ptr pointer;
typedef Ref reference;
reference operator * ( ) const { return (*node).data; }
pointer operator -> ( ) const { return &(operator*( )); }
...
};
list<Foo>::iterator ite;
...
*ite; //獲得一個Foo object;
ite->method( );//調用Foo::method( );
//相當于(*ite).method( );
//相當于(&(*ite))->method( );
迭代器本身就是一種指針,所以當用的時候,實際上是對該迭代器進行解引用,相當于讀取它所指的對象的值,所以可以看到重載運算符函數里面是返回data的。
而當調用->的時候,可以分成operator*()
被調用后,再被取值,為此相當于是先讀取所指對象的值,然后再取地址,所以返回是一個指針。
另外,迭代器和智能指針不同之處是迭代器還要重載++或者--,進行指針移動操作。
4.3. function-like classes: functor 仿函數
被派出差,回來更~