包含對象成員的類
valarray類是由頭文件valarray支持的。顧名思義,這個類用於處理數值,他支持諸如將數組中的所有元素的值想家以及在數組中找出最大和最小的值等操作。valarray被定義為一個模板類,以便能夠處理不同的數據類型。
一個Student類----一個getline導致構造函數遞歸的類
[cpp]
// readBook2.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "valarray"
#include "string"
class CStudent
{
public:
typedef std::valarray<double> ArrayDb;
CStudent() : sz_Name( "Null Student" ), m_ArrScores()
{
}
CStudent( const string& name ) : sz_Name( name ), m_ArrScores()
{
}
explicit CStudent( int n ) : sz_Name( "Nully" ), m_ArrScores( n )
{
}
CStudent( const string& name, int n ) : sz_Name( name ), m_ArrScores( n )
{
}
CStudent( const string& name, const ArrayDb& a ) : sz_Name( name ), m_ArrScores( a )
{
}
CStudent( const char* name, const double* pd, int n ) : sz_Name( name ), m_ArrScores( pd, n )
{
}
double Average() const
{
if ( m_ArrScores.size() > 0 )
{
return ( m_ArrScores.sum() / m_ArrScores.size() );
}
else
{
return 0;
}
}
const string& GetName() const
{
return sz_Name;
}
double& operator[]( int i)
{
return m_ArrScores[ i ];
}
double operator[]( int i ) const
{
return m_ArrScores[ i ];
}
ostream& CStudent::arr_out( ostream& os ) const
{
int i;
int lim = m_ArrScores.size();
if ( lim > 0 )
{
for ( i = 0; i < lim; i++ )
{
os << m_ArrScores[ i ] << " ";
if ( 4 == i % 5 )
{
os << endl;
}
}
if ( 0 != i % 5 )
{
os << endl;
}
}
else
{
os << "empty array";
}
return os;
}
friend istream& operator >>( istream& is, CStudent& stu );
friend istream& operator <<( istream& os, const CStudent& stu );
friend istream& getline( istream& is, const CStudent& stu );
~CStudent(){};
private:
string sz_Name;
ArrayDb m_ArrScores;
};
istream& operator >>( istream& is, CStudent& stu )
{
is >> stu.sz_Name;
return is;
}
ostream& operator <<( ostream& os, const CStudent& stu )
{
os << "this student name is:" << stu.GetName() << endl;
os << "this student scores is:" << endl;
stu.arr_out( os );
return os;
}
istream& getline( istream& is, const CStudent& stu )
{
getline( is, stu.sz_Name );
return is;
}
const int puplis = 3;
const int quizzes = 5;
void set( CStudent& sa, int n );
int _tmain(int argc, _TCHAR* argv[])
{
CStudent ada[ puplis ] = { CStudent( quizzes ), CStudent( quizzes ), CStudent( quizzes ) };
int i;
for ( i = 0; i < puplis; ++i )
{
set( ada[ i ], quizzes );
}
cout << "\nStudent List:" << endl;
for ( i = 0; i < puplis; ++i )
{
cout << ada[ i ].GetName() << endl;
}
cout << "\nResults:" << endl;
for ( i = 0; i < puplis; i++ )
{
cout << endl << ada[ i ];
cout << "average" << ada[ i ].Average() << endl;
}
cout << "Done." << endl;
return 0;
}
void set( CStudent& sa, int n )
{
cout << "Please enter the student name:" << endl;
getline( cin, sa );
cout << "Please enter " << n << "quiz scores:" << endl;
for ( int i = 0; i < n; i++ )
{
cin >> sa[ i ];
}
while( '\n' != cin.get() )
{
continue;
}
}
// 在
istream& getline( istream& is, const CStudent& stu )
{
getline( is, stu.sz_Name );
return is;
}
//const CStudent& stu導致遞歸
修改之後的版本
[cpp]
// readBook2.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "valarray"
#include "string"
class CStudent
{
public:
typedef std::valarray<double> ArrayDb;
CStudent() : sz_Name( "Null Student" ), m_ArrScores()
{
}
CStudent( const string& name ) : sz_Name( name ), m_ArrScores()
{
}
explicit CStudent( int n ) : sz_Name( "Nully" ), m_ArrScores( n )
{
}
CStudent( const string& name, int n ) : sz_Name( name ), m_ArrScores( n )
{
}
CStudent( const string& name, const ArrayDb& a ) : sz_Name( name ), m_ArrScores( a )
{
}
CStudent( const char* name, const double* pd, int n ) : sz_Name( name ), m_ArrScores( pd, n )
{
}
double Average() const
{
if ( m_ArrScores.size() > 0 )
{
return ( m_ArrScores.sum() / m_ArrScores.size() );
}
else
{
return 0;
}
}
const string& GetName() const
{
return sz_Name;
}
double& operator[]( int i)
{
return m_ArrScores[ i ];
}
double operator[]( int i ) const
{
return m_ArrScores[ i ];
}
ostream& CStudent::arr_out( ostream& os ) const
{
int i;
int lim = m_ArrScores.size();
if ( lim > 0 )
{
for ( i = 0; i < lim; i++ )
{
os << m_ArrScores[ i ] << " ";
if ( 4 == i % 5 )
{
os << endl;
}
}
if ( 0 != i % 5 )
{
os << endl;
}
}
else
{
os << "empty array";
}
return os;
}
friend istream& operator >>( istream& is, CStudent& stu );
friend istream& operator <<( istream& os, const CStudent& stu );
friend istream& getline( istream& is, CStudent& stu );
~CStudent(){};
private:
string sz_Name;
ArrayDb m_ArrScores;
};
istream& operator >>( istream& is, CStudent& stu )
{
is >> stu.sz_Name;
return is;
}
ostream& operator <<( ostream& os, const CStudent& stu )
{
os << "this student name is:" << stu.GetName() << endl;
os << "this student scores is:" << endl;
stu.arr_out( os );
return os;
}
istream& getline( istream& is, CStudent& stu )
{
getline( is, stu.sz_Name );
return is;
}
const int puplis = 3;
const int quizzes = 5;
void set( CStudent& sa, int n );
int _tmain(int argc, _TCHAR* argv[])
{
CStudent ada[ puplis ] = { CStudent( quizzes ), CStudent( quizzes ), CStudent( quizzes ) };
int i;
for ( i = 0; i < puplis; ++i )
{
set( ada[ i ], quizzes );
}
cout << "\nStudent List:" << endl;
for ( i = 0; i < puplis; ++i )
{
cout << ada[ i ].GetName() << endl;
}
cout << "\nResults:" << endl;
for ( i = 0; i < puplis; i++ )
{
cout << endl << ada[ i ];
cout << "average" << ada[ i ].Average() << endl;
}
cout << "Done." << endl;
return 0;
}
void set( CStudent& sa, int n )
{
cout << "Please enter the student name:";
getline( cin, sa );
cout << "Please enter " << n << "quiz scores:" << endl;
for ( int i = 0; i < n; i++ )
{
cin >> sa[ i ];
}
while( '\n' != cin.get() )
{
continue;
}
}
私有繼承
c++還有另一種實現has-a關系的途徑----私有繼承。使用私有繼承,基類的公有成員和保護成員都將成為派生類的私有成員。這意味著基類方法將不會成為派生對象公有接口的一部分,但可以在派生類的成員函數中使用它們。
使用公有繼承,基類的公有方法將成為派生類的公有方法。簡而言之,派生類將繼承基類的接口,這是is-a關系的一部分。使用私有繼承,基類的公有方法將成為派生類的私有方法。簡而言之,派生類不能繼承基類的接口。正如從被包含對象中看到的,這種不完全繼承是has-a關系的一部分。
因此私有繼承提供的特性與包含相同:獲得實現,但不獲得接口。所以,私有繼承也可以用來實現has-a關系。
[cpp]
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "valarray"
#include "string"
class CStudent : private valarray<double>, private string
{
private:
typedef std::valarray<double> ArrayDb;
public:
CStudent() : string( "Null Student" ), ArrayDb()
{
}
CStudent( const string& name ) : string( name ), ArrayDb()
{
}
explicit CStudent( int n ) : string( "Nully" ), ArrayDb( n )
{
}
CStudent( const string& name, int n ) : string( name ), ArrayDb( n )
{
}
CStudent( const string& name, const ArrayDb& a ) : string( name ), ArrayDb( a )
{
}
CStudent( const char* name, const double* pd, int n ) : string( name ), ArrayDb( pd, n )
{
}
~CStudent(){};
double Average() const
{
if ( ArrayDb::size() > 0 )
{
return ( ArrayDb::sum() / ArrayDb::size() );
}
else
{
return 0;
}
}
const string& GetName() const
{
return ( const string& ) *this;
}
double& operator[] ( int i )
{
return ArrayDb::operator[]( i );
}
const double operator[] ( int i ) const
{
return ArrayDb::operator[]( i );
}
ostream& arr_out( ostream& os ) const
{
int i;
int lim = ArrayDb::size();
if ( lim > 0 )
{
for ( i = 0; i < lim; i++ )
{
// os << ArrayDb[ i ] << " ";
os << ArrayDb::operator[]( i ) << " ";
if ( 4 == i % 5 )
{
os << endl;
}
}
if ( 0 != i % 5 )
{
os << endl;
}
}
else
{
os << "empty array";
}
return os;
}
friend istream& operator >>( istream& is, CStudent& stu );
friend istream& operator <<( istream& os, const CStudent& stu );
friend istream& getline( istream& is, CStudent& stu );
};
istream& operator >>( istream& is, CStudent& stu )
{
is >> ( string& )stu;
return is;
}
ostream& operator <<( ostream& os, const CStudent& stu )
{
os << "this student name is:" << stu.GetName() << endl;
os << "this student scores is:" << endl;
stu.arr_out( os );
return os;
}
istream& getline( istream& is, CStudent& stu )
{
getline( is, ( string& )stu );
return is;
}
const int puplis = 3;
const int quizzes = 5;
void set( CStudent& sa, int n );
int _tmain(int argc, _TCHAR* argv[])
{
CStudent ada[ puplis ] = { CStudent( quizzes ), CStudent( quizzes ), CStudent( quizzes ) };
int i;
for ( i = 0; i < puplis; ++i )
{
set( ada[ i ], quizzes );
}
cout << "\nStudent List:" << endl;
for ( i = 0; i < puplis; ++i )
{
cout << ada[ i ].GetName() << endl;
}
cout << "\nResults:" << endl;
for ( i = 0; i < puplis; i++ )
{
cout << endl << ada[ i ];
cout << "average" << ada[ i ].Average() << endl;
}
cout << "Done." << endl;
return 0;
}
void set( CStudent& sa, int n )
{
cout << "Please enter the student name:";
getline( cin, sa );
cout << "Please enter " << n << "quiz scores:" << endl;
for ( int i = 0; i < n; i++ )
{
cin >> sa[ i ];
}
while( '\n' != cin.get() )
{
continue; }
}
在CStudent類中,使用作用域解析操作符可以訪問基類的方法,但要使用基類對象本身,比如調用GetName()方法,他返回string對象成員name,但使用私有繼承時,該string對象沒有名稱,只能使用強制類型轉換。由於CStudent類是從string派生出來的,因此可以用過強制類型轉換,將CStudent對象轉換為string對象,結果為繼承而來的string對象。
[cpp]
const string& GetName() const
{
return ( const string& ) *this;
}
引用stu不會自動轉換為string引用,根本原因在於,在私有繼承中,不在進行顯示類型轉換的清華下,不能講指向派生類的引用或指針賦給基類引用或指針。不過,即使這個例子使用的是公有繼承,也必須使用顯示類型轉換。原因之一是,如果不適用類型轉換,代碼is >>stu;與友元函數原型匹配,從而導致遞歸調用:
[cpp]
istream& operator >>( istream& is, CStudent& stu )
{
is >> ( string& )stu;
return is;
}
另一個原因是,由於這個類使用的是多重繼承,編譯器將無法確定應轉換成哪個基類,如果兩個基類都提供了函數operator<<()。
使用包含還是私有繼承
由於既可以使用包含,也可以使用私有繼承來建立has-a關系。大多數c++程序員傾向於前者。不過私有繼承所提供的特性確實比包含多。例如,假設類包含保護成員,則這樣的成員在派生類中式可用的,但在繼承層次機構外是不可用的。如果使用組合獎這樣的類保護在另一類中,則後者將不是排成類,而是位於繼承層次結構之外,因此不能訪問保護成員。但通過繼承的到的將是派生類,因此他能夠訪問保護成員。
另一種需要使用私有繼承的情況是需要重新定義虛函數。派生類可以重新定義虛函數,但包含類不能。使用私有繼承,重新定義的函數將只能在類中使用,而不是公有的。
多重繼承(MI)
為了解決多重繼承而引入虛基類,也就是繼承的兩個類都含有相同函數的時候,產生無法識別該函數,引入了虛基類,只需要在調用某個父類的方法的時候,加上類名限定符即可
一個例子(注意這裡為了能夠進行顯示轉換需要使用virtual public方式繼承)
[cpp]
// testMI.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "string"
class Worker
{
public:
Worker( const string& Name, long Id ) : m_fullName( Name ), m_lId( Id )
{
}
Worker() : m_fullName( "no one" ), m_lId( 0L )
{
}
virtual ~Worker() = 0;
virtual void Set() = 0;
virtual void Show() const = 0;
protected:
void Data() const
{
cout << "Name:" << m_fullName << endl;
cout << "Employee ID:" << m_lId << endl;
}
void Get()
{
getline( cin, m_fullName );
cout << "Enter worker's ID:";
cin >> m_lId;
while ( '\n' != cin.get() )
{
continue;
}
}
private:
string m_fullName;
long m_lId;
};
Worker::~Worker()
{
}
class Waiter : virtual public Worker
{
public:
Waiter() : Worker(), m_nPanache( 0 )
{
}
Waiter( const string& Name, long Id, int p = 0 ) : Worker( Name, Id ), m_nPanache( p )
{
}
Waiter( const Worker& rWorker, int p = 0 ) : Worker( rWorker ), m_nPanache( p )
{
}
void Set()
{
cout << "Enter waiter's name:";
Worker::Get();
Get();
}
void Show() const
{
cout << "Category:waiter" << endl;
Worker::Data();
Data();
}
protected:
void Data() const
{
cout << "Panache rating:" << m_nPanache << endl;
}
void Get()
{
cout << "Enter waiter's Panache rating:";
cin >> m_nPanache;
while ( '\n' != cin.get() )
{
continue;
}
}
private:
int m_nPanache;
};
class Singer : virtual public Worker
{
public:
Singer() : Worker(), voice( 0 )
{
}
Singer( const string& Name, long Id, int v = 0 ) : Worker( Name, Id ), voice( v )
{
}
Singer( const Worker& rWorker, int v = 0 ) : Worker( rWorker ), voice( v )
{
}
void Set()
{
cout << "Enter singer's name:";
Worker::Get();
Get();
}
void Show() const
{
cout << "Category:singer" << endl;
Worker::Data();
Data();
}
protected:
enum{ other, alto, contralto, soprano, base, baritone, tenor };
enum{ Vtypes = 7 };
void Data() const
{
cout << "Vocal range:" << pv[ voice ] << endl;
}
void Get()
{
cout << "Enter number for singer's vocal range:" << endl;
int i;
for ( i = 0; i < Vtypes; i++ )
{
cout << i << ":" << pv[ i ] << " ";
if ( 3 == i % 4 )
{
cout << endl;
}
}
if ( 0 != i % 4 )
{
cout << endl;
}
cin >> voice;
while ( '\n' != cin.get() )
{
continue;
}
}
private:
static char* pv[ Vtypes ];
int voice;
};
char* Singer::pv[ Singer::Vtypes ] = { "other", "alto", "contralto", "Soprano", "bass", "baritone", "tenor" };
class SingingWaiter : public Singer, public Waiter
{
public:
SingingWaiter(){}
SingingWaiter( const string& Name, long Id, int p = 0, int v = other )
: Worker( Name, Id ), Waiter( Name, Id, p ), Singer( Name, Id, v )
{
}
SingingWaiter( const Worker& rWorker, int p = 0, int v = other )
: Worker( rWorker ), Waiter( rWorker, p ), Singer( rWorker, v )
{
}
SingingWaiter( const Waiter& rWaiter, int v = other )
: Worker( rWaiter ), Waiter( rWaiter ), Singer( rWaiter, v )
{
}
SingingWaiter( const Singer& rSinger, int p = 0 )
: Worker( rSinger ), Waiter( rSinger, p ), Singer( rSinger )
{
}
void Set()
{
cout << "Enter singing waiter's name:";
Worker::Get();
Get();
}
void Show() const
{
cout << "Category:singing waiter" << endl;
Worker::Data();
Data();
}
protected:
void Data() const
{
Singer::Data();
Waiter::Data();
}
void Get()
{
Waiter::Get();
Singer::Get();
}
};
const int SIZE = 5;
int _tmain(int argc, _TCHAR* argv[])
{
Worker* loals[ SIZE ];
int ct;
for ( ct = 0; ct < SIZE; ct++ )
{
char choice;
cout << "Enter the employee category:" << endl;
cout << "w:waiter s:singer " << "t:sing waiter q:quit" << endl;
cin >> choice;
while ( NULL == strchr( "wstq", choice ) )
{
cout << "Please enter a, w, s, t, or, q:";
cin >> choice;
}
if ( 'q' == choice )
{
break;
}
switch ( choice )
{
case 'w':
loals[ ct ] = new Waiter; break;
case 's':
loals[ ct ] = new Singer; break;
case 't':
loals[ ct ] = new SingingWaiter; break;
}
cin.get();
loals[ ct ]->Set();
}
cout << "\nHere is your staff:" << endl;
int i;
for ( i = 0; i < ct; i++ )
{
cout << endl;
loals[ i ]->Show();
}
for ( i = 0; i < ct; i++ )
{
delete loals[ i ];
}
cout << "Done." << endl;
return 0;
}
類模板
模板的聲明template<typename Type>,關鍵字typename告訴編譯器,將要定義一個模板。尖括號中的內容相當於函數的參數列表。可以把關鍵字typename看作是變量的類型名,該變量接受類型作為其值,把Type看做是該變量的名稱。
類模板裡面的成員函數,每個函數頭都將以相同的模板聲明打頭
除非編譯器實現了新的export關鍵字,否則將模板成員函數放置在一個獨立的實現文件中將無法運行。因為模板不是函數,它們不能單獨編譯。模板必須與特定的模板實例化請求一起使用。為此,最簡單的方法是將所有模板信息放在一個頭文件中,並在要使用這些模板的文件中包含該頭文件。
深入探討模板類
使用指針堆棧的方法之一是,讓調用程序提供一個指針數組,其中每個指針都指向不同的字符串。把這些指針放在堆棧中是有意義的,因為每個指針都將指向不同的字符串。注意,創建不同指針時調用程序的職責,而不是堆棧的職責。堆棧的任務是管理指針,而不是創建指針。
例如,假設要模擬下面的情況。某人將一車文件夾交付給了Plodson。如果Plodson的收取籃(in-basket)是空的,他將取出車中最上面的文件夾,將其放入收取籃(in-basket)中。如果收取籃既不是空的也不是滿的,Plodson將處理收取籃中最上面的文件,也可能取出車中的下一個文件,把它放入收取籃。他采取了自認為是比較魯莽的行為----仍硬幣來決定要才去的措施。
可以用一個指針數組來模擬這種情況,其中的指針指向表示車中文件的字符串。每個字符串都包含所描述的人的姓名。可以用堆棧表示收取籃,並使用第二個指針數組來表示發出籃。通過將指針從輸入數組壓入到堆棧中來表示將文件添加到收取籃中,同時通過從堆棧中彈出項目,並將它添加到發出籃中來表示處理文件。
堆棧類的模擬:
[cpp]
// testTemplate.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "string"
#include "ctime"
template<typename Type>
class CStack
{
public:
explicit CStack( int ss = SIZE );
CStack( const CStack& st );
~CStack()
{
if ( items )
{
delete[] items;
}
}
public:
bool isEmpty()
{
return ( 0 == m_nTop );
}
bool isFull()
{
return ( m_StackSize == m_nTop );
}
bool push( const Type& Item );
bool pop( Type& Item );
CStack& operator= ( const CStack& rCStack );
private:
enum { SIZE = 10 };
int m_StackSize;
Type* items;
int m_nTop;
};
template<typename Type>
CStack<Type>::CStack( int ss /* = SIZE */ ) : m_StackSize( ss ), m_nTop( 0 )
{
items = new Type[ m_StackSize ];
}
template<typename Type>
CStack<Type>::CStack( const CStack<Type>& st )
{
m_StackSize = st.m_StackSize;
m_nTop = st.m_nTop;
items = new Type[ st.m_StackSize ];
for ( int i = 0; i < m_StackSize; i++ )
{
items[ i ] = st.items[ i ];
}
}
template<typename Type>
bool CStack<Type>::push( const Type& Item )
{
if ( !isFull() )
{
items[ m_nTop ] = Item;
m_nTop++;
return true;
}
return false;
}
template<typename Type>
bool CStack<Type>::pop( Type& Item )
{
/*
if ( !isEmpty() )
{
Item = items[ m_nTop ]; // 注意這樣寫是不對的,因為未滿的時候items[ m_nTop ]指向未知
m_nTop--;
return true;
}*/
if ( m_nTop > 0)
{
Item = items[ --m_nTop ];
return true;
}
return false;
}
template<typename Type>
CStack<Type>& CStack<Type>::operator= ( const CStack<Type>& rCStack )
{
if ( rCStack == *this)
{
return *this;
}
if ( items )
{
delete[] items;
}
m_StackSize = st.m_StackSize;
m_nTop = st.m_nTop;
items = new Type[ st.m_StackSize ];
for ( int i = 0; i < m_StackSize; i++ )
{
items[ i ] = st.items[ i ];
}
}
const int NUM = 10;
int _tmain(int argc, _TCHAR* argv[])
{
srand( time( 0 ) );
cout << "Please enter stack size:";
int stacksize;
cin >> stacksize;
CStack< const char* > st( stacksize );
// in basket
const char* in[ NUM ] = {
"1:Hack Gilgamesh", "2:KiKi Ishtar",
"3:Betty Rocker", "4:Ian Flagranti",
"5:Wolfgang Kibble", "6:Portia Koop",
"7:Joy Almondo", "8:Xaverie Parika",
"9:Juan Moore", "10:Misha Mache"
};
// out basket
const char* out[ NUM ];
int processed = 0;
int nextin = 0;
while ( processed < NUM )
{
if ( st.isEmpty() )
{
st.push( in[ nextin++ ] );
}
else if ( st.isFull() )
{
st.pop( out[ processed++ ] );
}
else if ( rand() % 2 && nextin < NUM )
{
st.push( in[ nextin++ ] );
}
else
{
st.pop( out[ processed++ ] );
}
}
for ( int i = 0; i < NUM; i++ )
{
cout << out[ i ] << endl;
}
cout << "Done." << endl;
return 0;
}
數組模板范例和非類型參數
模板常被用作容器類,這是因為類型參數的概念非常適合於將相同的存儲方案用於不同的類型。確實,為容器類提供可重用代碼是引入模板的主要動機。
比如Array<double, 12> MyArray;其中的表達式參數可以是整形、枚舉、引用或指針。因此,double m是不合法的,但double* r是合法的。另外,模板代碼不能修改參數的值,也不能使用參數的地址。
可以將用於常規類的技術用於模板類。模板類可用作基類,也可用作組件類,還可用作其他模板的類型參數。
1.遞歸使用模板
[cpp]
// testTemplate2.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "string"
template<typename T, int n>
class MyArray
{
public:
MyArray(){}
explicit MyArray( const T& v );
public:
virtual T& operator[]( int i );
virtual T operator[]( int i ) const;
private:
T ar[ n ];
};
template<typename T, int n> // init
MyArray<T, n>::MyArray( const T& v )
{
for ( int i = 0; i < n; i++ )
{
ar[ i ] = v;
}
}
template<typename T, int n> // init
T& MyArray<T, n>::operator[]( int i )
{
if ( i < 0 || i >= n)
{
cerr << "Error in array limits:" << i << " is out of range" << endl;
exit( EXIT_FAILURE );
}
return ar[ i ];
}
template<typename T, int n> // init
T MyArray<T, n>::operator[]( int i ) const
{
if ( i < 0 || i >= n)
{
cerr << "Error in array limits:" << i << " is out of range" << endl;
exit( EXIT_FAILURE );
}
return ar[ i ];
}
int _tmain(int argc, _TCHAR* argv[])
{
MyArray<int ,10> sums;
MyArray<double, 10> aves;
MyArray< MyArray<int, 5>, 10 > twodee;
int i, j;
for ( i = 0; i < 10; i++ )
{
sums[ i ] = 0;
for ( j = 0; j < 5; j++ )
{
twodee[ i ][ j ] = ( i + 1 ) * ( j + 1 );
sums[ i ] += twodee[ i ][ j ];
}
aves[ i ] = ( double )sums[ i ] / 10;
}
for ( i = 0; i < 10; i++ )
{
for ( j = 0; j < 5; j++ )
{
cout.width( 2 );
cout << twodee[ i ][ j ] << " ";
}
cout << ":sum = ";
cout.width( 3 );
cout << sums[ i ] << ", average = " << aves[ i ] << endl;
}
cout << "Done." << endl;
return 0;
}
MyArray< MyArray<int, 5>, 10 > twodee;這使得twodee是一個包含10個元素的數組,其中每個元素都是一個包含5個int元素的數組。與之等價的常規數組聲明如下:int twodee[ 10 ][ 5 ];在模板句法中,維的順序與等價的二維數組相反。
2.模板中使用多個類型參數
模板可以保護多個類型參數。例如,假設希望類可以保存兩種值,則可以創建並使用Pair模板來保存兩個不同的值(標准模板庫提供了類似的模板,名為pair)。
[cpp]
// testPair.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "string"
template<typename T1, typename T2>
class CPair
{
public:
CPair(){}
CPair( const T1& aval, const T2 bval ) : a( aval ), b( bval )
{
}
public:
T1& first()
{
return a;
}
T2& second()
{
return b;
}
T1& first() const { return a }
T2& second() const { return b }
private:
T1 a;
T2 b;
};
int _tmain(int argc, _TCHAR* argv[])
{
CPair<string, int> ratings[ 4 ] =
{
CPair<string, int>( "The Purple Duke", 5 ),
CPair<string, int>( "Jake's Frisco Al Fresco", 4 ),
CPair<string, int>( "Mont Souffle", 5 ),
CPair<string, int>( "Gertie's Eats", 3 ),
};
int joins = sizeof( ratings ) / sizeof( CPair<string, int> );
cout << "Raring:\tEatery\n";
for ( int i = 0; i < joins; i++ )
{
cout << ratings[ i ].second() << ":\t" << ratings[ i ].first() << endl;
}
cout << "Oops ! Revised rating:" << endl;
ratings[ 3 ].first() = "Gertie's Fab Eat";
ratings[ 3 ].second() = 6;
cout << ratings[ 3 ].second() << ":\t" << ratings[ 3 ].first() << endl;
cout << "Done." << endl;
return 0;
}
模板的具體化
類模板與函數模板很相似,因為可以有隱式實例化、顯示實例化和顯示具體化,他們統稱為具體化(specialization)。模板以通用類型的方式描述類,而具體化是使用具體的類型生成類聲明。
1.隱式實例化
一般的永華都是隱式實例化(implicit instantiation),即他們聲明一個活多個對象,指出所需的類型,而編譯器使用通用模板提供的處方生成具體的類定義
2.顯示實例化
當使用關鍵字template並支出所需類型來聲明類時,編譯器將生成類聲明的顯示實例化(explicit insantiation)。聲明必須位於模板定義所在的名稱空間中。例如,
template class MyArray<string, 100>
將MyArray<string, 100>聲明為一個類。在這種情況下,雖然沒有創建或提及類對象,編譯器也將生成類聲明(包括方法定義)。和隱式實例化一樣,也將根據通用模板來生成具體化。
3.顯示具體化(explicit specialization)
顯示具體化是特定類型(用於替換模板的通用類型)的定義。有時候,可能需要在為特殊類型實例化時,對模板進行修改,使其行為不同。在這種情況下,可以創建顯示具體化。例如:
template<typename T>
class CSortedArray
{};
在排序的時候,使用>操作符來對值進行比較。對於數字,這管用;如果T表示一種類,則只用定義了T::operator>()方法,這也管用;但如果T是有char*表示的字符串,這將不管用。所以需要對其具體化
template<> class CSortedArray<char *>
{};
4.部分具體化
c++還允許部分具體化(partial specialization),即部分限制模板的通用性。例如,部分具體化可以給類型參數之一指定具體的類型
template<class T1, class T2> class CPair {};
template<class T1> class CPair<T1, int> {}:
關鍵字後面的<>聲明的是沒有被具體化的類型參數。因此,上述第二個聲明將T2具體化為int,但T1保存不變。注意,如果指定所有的類型,則<>內將為空,這將導致顯示具體化。
template<> class CPair<int, int> {};
[cpp]
// testParSpe.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "iostream"
using namespace std;
template< typename T1, typename T2 >
class Test
{
public:
Test( T1 a, T2 b )
{
m_a = a;
m_b = b;
}
void show()
{
cout << "the a value is:" << m_a << endl;
cout << "the b value is:" << m_b << endl;
}
private:
T1 m_a;
T2 m_b;
};
template< typename T1 >
class Test< T1, int >
{
public:
Test( T1 a, int b )
{
m_a = a;
m_b = b;
}
void show()
{
cout << "this template< typename T1 >, the a value is:" << m_a << endl;
cout << "this template< typename T1 >, the b value is:" << m_b << endl;
}
private:
T1 m_a;
int m_b;
};
int _tmain(int argc, _TCHAR* argv[])
{
Test< double, int > test( 20.1, 4 );
test.show();
return 0;
}
成員模板
C++模板支持的另一個新特性是:模板可用作結構、類或模板類的成員。要完成時限STL的設計,必須要使用這項特性。
[cpp]
// testTemplateFriend.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "string"
template <typename T>
class CBeta
{
public:
CBeta( T t, int i ) : q( t ), n( i )
{}
template<typename U>
U blab( U u, T t )
{
return ( n.Value() + q.Value() ) * u / t;
}
void show() const
{
q.show();
n.show();
}
private:
template<typename V>
class CHold
{
public:
CHold( V v = 0 ) : val( v ){}
void show() const { cout << val << endl; }
V Value() const { return val; }
private:
V val;
};
CHold<T> q;
CHold<int> n;
};
int _tmain(int argc, _TCHAR* argv[])
{
CBeta<double> quy( 3.5, 3 );
quy.show();
cout << quy.blab( 10, 2.3 ) << endl;
cout << "Done." << endl;
return 0;
}
在這個程序中CHold模板是在私有部分聲明的,因此只能在CBeta類中訪問他。CBeta類使用CHold模板聲明了兩個數據成員:
CHold<T> q;
CHold<int> n;
n是基於int類型的CHold對象,而q成員是基於T類型(CBeta模板參數)的CHold對象。
將模板用作參數
模板可以包含類型參數(如typename T)和非類型參數(例如int n)。模板還可以包含本身就是模板的參數。這種參數是模板新增的特性,用於實現STL。
[cpp]
// testTemplateParam.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "string"
#include "ctime"
template<typename Type>
class CStack
{
public:
explicit CStack( int ss = SIZE );
CStack( const CStack& st );
~CStack()
{
if ( items )
{
delete[] items;
}
}
public:
bool isEmpty()
{
return ( 0 == m_nTop );
}
bool isFull()
{
return ( m_StackSize == m_nTop );
}
bool push( const Type& Item );
bool pop( Type& Item );
CStack& operator= ( const CStack& rCStack );
private:
enum { SIZE = 10 };
int m_StackSize;
Type* items;
int m_nTop;
};
template<typename Type>
CStack<Type>::CStack( int ss /* = SIZE */ ) : m_StackSize( ss ), m_nTop( 0 )
{
items = new Type[ m_StackSize ];
}
template<typename Type>
CStack<Type>::CStack( const CStack<Type>& st )
{
m_StackSize = st.m_StackSize;
m_nTop = st.m_nTop;
items = new Type[ st.m_StackSize ];
for ( int i = 0; i < m_StackSize; i++ )
{
items[ i ] = st.items[ i ];
}
}
template<typename Type>
bool CStack<Type>::push( const Type& Item )
{
if ( !isFull() )
{
items[ m_nTop ] = Item;
m_nTop++;
return true;
}
return false;
}
template<typename Type>
bool CStack<Type>::pop( Type& Item )
{
/*
if ( !isEmpty() )
{
Item = items[ m_nTop ]; // 注意這樣寫是不對的,因為未滿的時候items[ m_nTop ]指向未知
m_nTop--;
return true;
}*/
if ( m_nTop > 0)
{
Item = items[ --m_nTop ];
return true;
}
return false;
}
template<typename Type>
CStack<Type>& CStack<Type>::operator= ( const CStack<Type>& rCStack )
{
if ( rCStack == *this)
{
return *this;
}
if ( items )
{
delete[] items;
}
m_StackSize = st.m_StackSize;
m_nTop = st.m_nTop;
items = new Type[ st.m_StackSize ];
for ( int i = 0; i < m_StackSize; i++ )
{
items[ i ] = st.items[ i ];
}
}
template< template<typename T> class Thing >
class Crab
{
public:
Crab(){}
bool push( int iA, double fB )
{
return ( s1.push( iA ) && s2.push( fB ) );
}
bool pop( int& iA, double& fB )
{
return ( s1.pop( iA ) && s2.pop( fB ) );
}
private:
Thing<int> s1;
Thing<double> s2;
};
int _tmain(int argc, _TCHAR* argv[])
{
Crab<CStack> nebula;
int nj;
double nb;
cout << "Enter int double pairs, such as 4 3.5 ( 0 0 to be end):" << endl;
while ( cin >> nj >> nb && nj > 0 && nb > 0 )
{
if ( !nebula.push( nj, nb ) )
{
break;
}
}
while ( nebula.pop( nj, nb) )
{
cout << nj << ", " << nb << endl;
}
cout << "Done." << endl;
return 0;
}
在這個類裡還可以使用模板參數和常規參數,例如:
template< template<typename t> class Thing, typename U, typename V>
class Crab
{
private:
Thing<U> s1;
Thing<V> s2;
}
模板類和友元
模板類聲明也可以有友元。模板的友元分3類:
1.非模板友元
[cpp]
// testTemplateFriend2.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "string"
template<typename T>
class CHasFriend
{
public:
CHasFriend( const T& t ) : item( t )
{
ct++;
}
~CHasFriend()
{
ct--;
}
friend void counts();
friend void report( const CHasFriend<T>& );
private:
explicit CHasFriend( const CHasFriend& rCHasFriend ){}
private:
T item;
static int ct;
};
template<typename T>
int CHasFriend<T>::ct = 0;
void counts()
{
cout << "int count:" << CHasFriend<int>::ct;
cout << " double count:" << CHasFriend<double>::ct << endl;
}
void report( const CHasFriend<int>& hf )
{
cout << "CHasFriend item:" << hf.item << endl;
}
void report( const CHasFriend<double>& hf )
{
cout << "CHasFriend item:" << hf.item << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
cout << "No objects declared:";
counts();
CHasFriend<int> hfil1( 10 );
cout << "After hfil1 declared:";
counts();
CHasFriend<int> hfil2( 20 );
cout << "After hfil2 declared:";
counts();
CHasFriend<double> hfdb( 10.5 );
cout << "After hfdb declared:";
counts();
report( hfil1 );
report( hfil2 );
report( hfdb );
return 0;
}
代碼中聲明使counts()函數成為模板所有實例化的友元。它內部的CHasFriend<int>和CHasFriend<double>是針對不同的模板的。
2.約束(bound)模板友元,即友元的類型取決於類呗實例化時的類型
[cpp]
// testTemplateFriend3.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "string"
template<typename T>
void counts()
{
cout << "this count value is:" << CHasFriend<T>::ct << endl;
}
template<typename T>
void report( const T& hr )
{
cout << "this count value is:" << hr.m_item << endl;
}
template<typename TT>
class CHasFriend
{
public:
CHasFriend( const TT item ) : m_item( item )
{
ct++;
}
~CHasFriend()
{
ct--;
}
friend void counts<TT>();
friend void report<>( const CHasFriend<TT>& hr );
private:
explicit CHasFriend( const CHasFriend& rCHasFriend ){}
private:
TT m_item;
static int ct;
};
template<typename T>
int CHasFriend<T>::ct = 0;
int _tmain(int argc, _TCHAR* argv[])
{
counts<int>();
CHasFriend<int> hfil( 10 );
CHasFriend<int> hfi2( 20 );
CHasFriend<double> hfdb( 10.5 );
report( hfil );
report( hfi2 );
report( hfdb );
cout << "counts<int>() output :" << endl;
counts<int>();
cout << "counts<double>() output :" << endl;
counts<double>();
return 0;
}
聲明中的<>指出這是模板具體化。對於report(),<>可以為空,這是因為可以從函數參數推斷出模板類型參數(CHasFriend<TT>)。不過,也可以使用report< CHasFriend<TT> >( CHasFriend<TT>& )。但counts()函數沒有參數,因此必須使用模板參數句法(<TT>)來指明其具體化。還需要注意的是,TT是CHasFriend類的參數類型。
3.非約束(unbound)模板友元,即友元的所有具體化都是類的每一個具體化的友元
[cpp]
// testTemplateFriend4.cpp : 定義控制台應用程序的入口點。
//
#include "stdafx.h"
#include "iostream"
using namespace std;
#include "string"
template<typename T>
class CManyFriend
{
public:
CManyFriend( const T& i ) : item( i )
{
}
template<typename C, typename D> friend void show( C&, D& );
private:
T item;
};
template<typename C, typename D>void show( C& c, D& d )
{
cout << c.item << " " << d.item << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
CManyFriend<int> hfil( 10 );
CManyFriend<int> hfil2( 20 );
CManyFriend<double> hfdb( 10.5 );
cout << "hfil, hfil2:";
show( hfil, hfil2 );
cout << "hfil2, hfdb:";
show( hfil2, hfdb );
return 0;
}
前面的1、2,int類具體化獲得int函數具體化,依此類推。通過在類內部聲明模板,可以創建非約束友元函數,即每個函數具體化都是每個類具體化的友元。對於非約束友元,友元模板類型參數與模板類類型參數是不同的:
template<typename C, typename D> friend void show( C&, D& );
但前提是必須在類內部聲明模板