`
evasiu
  • 浏览: 168973 次
  • 性别: Icon_minigender_2
  • 来自: 广州
博客专栏
Fa47b089-e026-399c-b770-017349f619d5
TCP/IP详解卷一>阅读...
浏览量:12524
社区版块
存档分类
最新评论

C++ premier -- 模板与泛型编程

 
阅读更多

所谓泛型编程就是以独立于任何特定类型的方式编写代码,使用泛型程序时,我们需要提供具体程序实例所操作的类型或值。泛型编程与面向对象编程一样,都依赖于某种形式的多态性。模板是泛型编程的基础,模板是创建类或函数的蓝图或公式。例如标准库定义了一个类模板,该模板定义了vector的含义,vector是用于装载同种类型的元素的容器,装载的对象是多态的,vector<int>装有很多int元素,vector<string>则装了string,但是它们有很多相似的操作,由此形成了一个模板类。“面向对象编程所依赖的多态性称为运行时多态性,泛型编程所依赖的多态性称为编译时多态性或参数式多态性。”从这句话我们可以看出,泛型编程在编译时决定使用多态中的哪一态。

单独的函数可以是一个模板,如编写一个函数比较两个值并指出第一个值是小于、等于还是大于第二个值,可以定义如下函数模板:

template<typename T> int compare( const T& a, const T& b ){
	if( a > b )
		return 1;
	if( a < b )
		return -1;
	return 0;
}

template表明这是一个函数模板,而<>里面的列表就像函数的形参列表一样,表示一种未知的,待用户提供的参数,称为模板形参。函数的形参在运行时提供实参,而模板的形参在编译时提供模板实参。这样调用函数模板:

int main(){
	int a = 0, b = 1;
	double c = 30.1, d = 40.5;
	compare( a, b );  //compare( const int&, const int& ) is instantiated
	compare( c, d );	//compare( const double&, const double& ) is instantiated
	compare( a, c );	//error: no matching function for compare( const int&, const double& )
}

调用上面函数模板的时候,编译器通过推断确定哪些模板实参绑定到模板形参。由于是先推断,后再用函数实参调用实例化后的函数,因此类型形参的实参转换是受限的。如上面代码最后一个例子编译时会出错。
类型形参的实参受限转换:
一般而言,不会转换实参以匹配已有的实例化,相反,会产生新的实例。除了产生新的实例化之外,编译器只会执行两种转换:
1. const转换:接受const引用或const指针的函数可以分别用非const对象的引用或指针来调用,无须产生新的实例化。如果函数接受非引用类型,形参类型和实参都忽略const,即无论传递const或非const对象给接受非引用类型的函数,都使用相同的实例化。
2. 数组或函数到指针的转换:如果模板形参不是引用类型,则对数组或函数类型的实参应用常规指针转换。数组实参将当作指向其第一个元素的指针,函数实参当作指向函数类型的指针。
示例如下:

template <typename T> void fobj( T a, T b ){  }
template <typename T> void fref( const T& a, const T& b ){ }
string s1("a value");
const string s2( "another value" );
fobj( s1, s2 ); //ok:calls function fobj( string, string ), const is ignored
fref( s1, s2 ); //ok:calls function fref( const string&, const string& ), s1 converted to const reference
int a[10], b[42];
fobj( a, b ); //ok:calls function fobj( int*, int* );
fref( a, b ); //error: no matching function for fref( int&[10], int&[42] )

模板形参除了用typename或class声明的类型形参外,还有用内置类型或自定义类型声明的非类型形参。如下面的函数:

template <typename T, size_t N> void printArray( const T (&array)[N] ){	
	for( size_t t=0; t!=N; ++t )
		cout<<array[t]<<" ";
	cout<<endl;
}
//调用函数:
const double a[3] = {2, 3, 4.3};
//这里我有一个问题就是,如果把a声明为double a[3]的话,编译会报错:no matching function for printArray( double[3] )
printArray( a );	//instantiate printArray( const double(&)[3] )

typename T是一种类型形参,而size_t N是一种非类型形参,当用下面两个语句调用该函数时,T在编译时初始化为double而N则初始化为3。

类也可以做成一个模板,例如vector,声明方式也是在class前先将其声明为template并接着声明其模板形参列表,使用的时候则是直接在该类名后面加上实参列表,如vector<int>。类模板中可以出现三种友声明,每一种都声明了与一个或多个实体的友元关系:
(1)普通非模板类或函数的友元声明,将友元关系授予明确指定的类或函数
(2)类模板或函数模板的友元声明,授予对友元所有实例的访问权
(3)只授予对类模板或函数模板的特定实例的访问权的友元声明。

template <class T> class Bar{
	//grants access to ordinary, nontemplate class and function
	friend class FooBar;
	friend void fcn();
	//...
};

template <class T> class Bar2{
	//grant access to Foo1 or template_fcn1 parameterized by any type
	template<class T2> friend class Foo1;
	template<class T2> friend void template_fcn1( const T2& );
	//...
};

//declaration of template class Foo2 and tempalte function template_fcn
template <class T> class Foo2;
template <class T> void template_fcn( const T& );
template <class T> class Foo{
	//grants access to a single specific instance parameterized by char*
	friend class Foo2<char*>;
	friend void template_fcn2<char*> (char* const& );
	//...
}

任意类可以拥有本身为类模板或函数模板的成员,这种成员称为成员模板,成员模板不能为虚。

我们通常会将函数声明、类声明放在头文件中而其定义放在另一些cpp文件中。如何让编译器知道函数的具体实现呢?书中介绍有两种编译模型:一是包含编译模型,在声明模板的头文件最后指明,用#include ***.cpp表示。二是分别编译模型,在模板的定义前加入export关键字。不同编译器可能会使用不同的编译模型,例如我用的是g++,使用的是包含编译模型。

最后一个话题是模板特化。模板特化可以针对模板函数,也可以针对类,可以特化整个类,也可以特化类中的一些成员。特化函数仍然举一开始的compare函数。试想如果我们这样调用该模板函数:

const char* a = "hello", *b = new char[6];
b = "hello";
compare( a, b );

此时调用的函数为compare( const char*, const char* ),而在函数体内真正进行比较的是两个指针的地址,返回的结果不具备任何意义。我们需要为const char*版本专业定义一种新的比较方法,该模板函数的模板特化如下:

template<>
int compare<char*>( char* const& a, char* const& b ){
	//cout<<"specific"<<endl;
	return strcmp( a, b );
}

 下面用一个自定义的Queue类说明前面提到的关于类的模板以及一些特化:

 

//Queue.h

#ifndef QUEUE_H
#define QUEUE_H

#include<iostream>

template<class Type> class Queue;
template<class Type> std::ostream& operator<<( std::ostream& out, const Queue<Type> &q );
std::ostream& operator<<( std::ostream& out, const Queue<const char*> &q );

template<class Type> class QueueItem{
	//声明友元
	friend class Queue<Type>;
	friend std::ostream& operator<< <Type> ( std::ostream&, const Queue<Type>& );
	//private class, no public members.
	private:
		QueueItem( const Type& t ): item(t), next(0){ }
		Type item;
		QueueItem* next;
	};
	
template<class Type> class Queue{
	friend std::ostream& operator<< <Type> ( std::ostream&, const Queue<Type>& );
	public:
		Queue():head(0),tail(0){  }
		template<class It> Queue( It beg, It end ):head(0),tail(0){
			copy_elems( beg, end );
		}
		Queue( const Queue &Q ):head(0),tail(0){
			copy_elems( Q );
		}
		Queue& operator=( const Queue& rhs );
		~Queue(){ destroy(); }
		template<class Iter> void assign( Iter, Iter );
		Type& front(){
			return head->item;
		}
		const Type& front() const{
			return head->item;
		}
		void push( const Type& );
		void pop();
		bool empty() const{
			return head == 0;
		}
	private:
		QueueItem<Type>* head;
		QueueItem<Type>* tail;
		void destroy();
		void copy_elems( const Queue& );
		template<class Iter> void copy_elems( Iter, Iter );
	};

//为特化类Queue<const char*>定义的重载函数	
template<> std::ostream& operator<<( std::ostream& out, const Queue<const char*> &q )
template<> class Queue<const char*>{
	friend std::ostream& operator<<( std::ostream&, const Queue<const char*>& );
	public:
		Queue<const char*>(){ std::cout<<"specific"<<std::endl; }
		void push( const char* );
		void pop(){ real_queue.pop(); }
		bool empty() { return real_queue.empty(); }
		std::string front(){ return real_queue.front(); }
		const std::string front() const { return real_queue.front(); }
	private:
		Queue<std::string> real_queue;
		};

//包含模型
#include "Queue.cpp"
#endif
//Queue.cpp
template<class Type> void Queue<Type>::destroy(){
	while( !empty() )
		pop();
	}
	
template<class Type> void Queue<Type>::pop(){
	QueueItem<Type>* p = head;
	head = head->next;
	delete p;
}

template<class Type> void Queue<Type>::push( const Type& elem ){
	QueueItem<Type>* p = new QueueItem<Type>( elem );
	if( empty() )
		head = tail = p;
	else{
		tail->next = p;
		tail = p;
	}
}

template<class Type> void Queue<Type>::copy_elems( const Queue& Q ){
	for( QueueItem<Type>* p=Q.head; p; p=p->next )
		push(p->item);
	}
	
template<class Type> Queue<Type>& Queue<Type>::operator=( const Queue& rhs ){
	if( this != &rhs ){
		while( !empty() )
			pop();
		copy_elems( rhs );
	}
	return *this;
}

template<class Type> template<class It> 
void Queue<Type>::assign( It beg, It end){
	destroy();
	copy_elems( beg, end );
}

template<class Type> template<class It>
void Queue<Type>::copy_elems( It beg, It end ){
	while( beg != end ){
		push(*beg);
		++beg;
	}
}

//实现特化类的成员函数
void Queue<const char*>::push( const char* val ){
	real_queue.push( val );
}

/*如果只是特化类中的成员函数,方式如下:

template<> void Queue<const char*>::push( const char* const& val ){
	std::cout<<"specific"<<std::endl;
	char* new_item = new char[strlen(val)+1];
	strncpy( new_item, val, strlen(val)+1 );
	QueueItem<const char*> *q = new QueueItem<const char*>(new_item);
	if( empty() )
		head = tail = q;
	else{
		tail->next = q;
		tail = q;
	}
}

template<> void Queue<const char*>::pop(){
	std::cout<<"specific"<<std::endl;
	QueueItem<const char*> *p = head;
	delete head->item;
	head = head->next;
	delete p;
}

*/

template<class Type> std::ostream& operator<<( std::ostream& out, const Queue<Type> &q ){
	out<<"<";
	QueueItem<Type>* p;
	for( p = q.head; p; p=p->next )
		out<<p->item<<" ";
	out<<">";
	return out;
}

std::ostream& operator<<( std::ostream& out, const Queue<const char*> &q ){
	return out<<q.real_queue;
}




 

分享到:
评论

相关推荐

    C++ premier中文版 电子书

    C++不仅保留了C语言的效率和灵活性,还引入了类、模板、异常处理、命名空间等面向对象的概念,以及泛型编程和 STL(标准模板库)等现代C++特性。 《C++ Primer》第四版中英文对照_chm.chm文件是一个压缩包,其中...

    C++ Premier第四版中文英文源代码

    4. **模板**:模板是C++中实现泛型编程的关键工具,允许我们编写能处理不同类型数据的通用代码。书中会介绍函数模板和类模板,以及模板特化和部分特化。 5. **异常处理**:C++的异常处理机制为程序提供了错误处理的...

    c++ premier 第四版 课后习题答案+所有源代码

    《C++ Primer 第四版》的习题涵盖广泛,从基础语法到面向对象编程,再到STL(标准模板库)和泛型编程,都有所涉及。习题解答提供了正确答案和思路,有助于读者自我检查,理解错误之处,并学习如何解决实际问题。 源...

    C++Primer(中文版第4版)+源码+习题答案完整版.7z

    模板不仅用于函数,还可以用于类,形成泛型编程的基础。读者将学习到如何创建和使用函数模板、类模板,以及模板特化和模板元编程等高级技术。 C++ Primer还包括了异常处理和标准库的使用,这两个方面在实际编程中至...

    C++ Premier 中英文对照chm版

    3. **模板**:模板是C++的泛型编程工具,它可以用于创建函数模板和类模板,实现代码复用,提高效率。 4. **STL(Standard Template Library)**:C++的标准模板库提供了容器(如vector、list)、迭代器、算法和函数...

    C++从入门到精通(笔者收集的十几本C++相关教程)

    该资源是笔者收集的C++相关教程,从入门到精通,覆盖十几门电子书。给那些想学习C++而找不...泛型编程与STL.pdf; 美河提供.STL.源码剖析.pdf; 高质量C++C编程指南.pdf; 计算机算法导引:设计与分析(卢开澄).pdf;

    The C++Standard Library(2nd).pdf

    此外,本书还适合与《C++ premier(5th)》配合使用,意味着它可能涉及了C++的基础知识和一些高级特性,使得读者在掌握了基础C++编程技能后,能够进一步深化对标准库的理解和应用。 在描述中还提到,这本书不仅是一...

    [Premier]C++_Programming_For_The_Absolute_Beginner.zip

    C++也支持模板,允许创建泛型代码,这样同一段代码可以应用于不同数据类型。同时,标准模板库(STL)提供了容器(如vector、list、set、map)、算法和迭代器,极大地简化了编程工作。 异常处理是C++中处理错误和...

Global site tag (gtag.js) - Google Analytics