1. 程式人生 > 其它 >C++知識點61——typename與class、模板程式設計與繼承、模板類和友元、類模板與static成員

C++知識點61——typename與class、模板程式設計與繼承、模板類和友元、類模板與static成員

技術標籤:C++基礎c++

一、typename與class的異同

1、啥時候既可以使用typename,又可以使用class?

當表示模板引數的時候,二者沒有區別

2、啥時候只能使用typename,不能使用class?

當模板內部的成員表示一個型別時,必須使用typename,而不能使用class

雖然書上是這樣寫的,但是實際並不使這樣,在g++ 7.5.0測試發現,即使表示一個模板引數的型別成員,typename和class也沒有明顯差異

示例1

template <class T>
class mytest
{
	class T::val_type func(const T &c);
};

template <class T>
class T::val_type mytest<T>::func(const T &c)
{
	typename T::val_type *ptr;
    class T::val_type t;
	return typename T::val_type();
}

上述程式碼中的第10行必須加typename或者class關鍵字,否則,編譯器會認為型別T中的static成員val_type與ptr相乘,而ptr沒有定義,然而使用typename或者class都可以

但是,第11行只能用typename,不能用class,否則編譯器報錯

編譯器提示,和class一起使用的表示式不正確,所以必須typename的原因是class後面的表示式不正確,而不是什麼只能用typename不能用class

示例2

template <typename T>
void print(const T &con)
{
	class T::const_iterator pos;
	class T::const_iterator end(con.end());

	for (pos=con.begin();pos!=end;++pos)
	{
		cout<<*pos<<endl;
	}
}

上述程式碼負責列印容器中的元素,第4,5行程式碼雖然都是表示T中的迭代器型別,但是使用class和typename都可以

所以,使用模板引數中的型別成員時,用class還是typename沒有區別,只有class後的表示式不正確時,才必須使用typename

所以,既然能使用class的地方都能使用typename,但是有的地方還不能使用class,所以,在模板程式設計中,只使用typename表示型別就對了

二、模板程式設計與繼承

如果一個類模板被當做基類,如果子類不是模板類,那麼派生列表要指定基類的模板實參,如果子類也是個類模板,那麼,子類的派生列表可以使用自身的模板引數來指定基類的模板實參

示例

template <typename T>
class base
{
};

template <typename T>
class derive:public base<T>
{
};

class derive2:public base<int>
{
};

如果子類是個類模板,並且派生列表中的基類使用了子類的模板引數,那麼在子類成員函式中呼叫基類成員函式時,要指定作用域

template <typename T>
class base
{
public:
	void functest(){cout<<__func__<<"in base"<<endl;}
};

template <typename T>
class derive:public base<T>
{
public:
	void func(){
		functest();
		cout<<__func__<<"in derive"<<endl;
	}
};

上述程式碼無法編譯通過

編譯時,編譯器提示functest是個依賴模板引數的函式,但是沒有指明模板引數,所以編譯器報錯。所以解決辦法有兩個:

1、加上基類的作用域

emplate <typename T>
class derive:public base<T>
{
public:
	void func(){
		base<T>::functest();
		cout<<__func__<<"in derive"<<endl;
	}
};

2、在子類中重新定義一個functest

template <typename T>
class derive:public base<T>
{
public:
	void func(){
		functest();
		cout<<__func__<<"in derive"<<endl;
	}
	void functest(){cout<<__func__<<"in derive"<<endl;}
};

如果子類只是個普通類,但是要類模板做基類,那麼必須將基類模板例項化

class derive2:public base<int>
{
public:
	void func(){
		functest();
		cout<<__func__<<"in derive"<<endl;
	}
};

此時編譯器並不會報錯,因為基類是個具體的類,建立該子類物件時,基類的this已經確定,可以找到基類的的functest

三、模板類和友元

如果一個類模板中包含一個友元,如果友元不是模板,那麼,友元可以訪問所有模板的例項,無須解釋,和以前一樣

如果友元是模板,那麼友元模板的所有或者部分例項可以訪問所有的類模板的例項

示例

template <typename T>
class friend1
{

};

template <typename T>
class friend2
{

};

template <typename T1>
class test2
{
	template <typename T2>
	friend class friend1;//類模板friend1的所有例項都是test2例項的友元

	friend class friend2<T1>;//只有用T1例項化的類才是test2的友元
};

四、類模板與static成員

類模板的每個例項類都各自擁有類模板中的static成員

示例

template <typename T>
class statictest
{
	static void func() {}
	static int si;
};

template <typename T>
int statictest<T>::si=0;

int main(int argc, char const *argv[])
{
	statictest<int> t;
	int i=t.si;
	//int i2=statictest::si;
}

因為static成員是各個例項化的模板類各自所有,而上述程式碼中的第15行沒有指定模板引數,需要註釋掉

static成員函式和其他類模板的成員函式一樣,只有在被呼叫時才會被例項化產生程式碼

參考

《C++ Template》

《C++ Primer》

歡迎大家評論交流,作者水平有限,如有錯誤,歡迎指出