分享

今年网易最后一道C++笔试题是考了这样一道题目

 暖风醉伊人 2012-11-20

2011年5月20日  

|字号 订阅

    今年网易最后一道C++笔试题是考了这样一道题目:C++的traits是什么机制,有什么用?请举例说明。

    我没答上来,回来查了一下,才发现是和STL泛化编程相关的。从网上找来两篇候捷的大作一读,才有点明白。现在写下来,看我是否真的理解了。首先,我们来了解一下什么是泛化编程。

      一般泛型编程时,比如我设计一个算法:

template<class I, class T>
I find(I first, I end, T& value)
{
   while( first != end && *first != value) //需要重载iterator间的“!= *提领”算子,重载T间的比较算子
           first++;//需要重载后置式++算子
   return first;
}

first,end是class,一般就是iterator,而class T就是iterator所指之物的类型;在这个模范函数里,我们声明了两个类型I,T。事实上,I与T是相关的,比如int*与int。比如我有一个

struct node
{
   int val;
   node *pnext;
};

在上面需要运用find算法,就需要一个iterator包装,在这里我申明一个类模板:

template<class T>
struct Node{//C++中struct与class的区别在于struct中members默认access level是public,class是private
      T *ptr;
     Node(const T* p):ptr(p){}
     T& operator*() const { return *ptr; }//重载*提领算子,返回的是T类型
     T* operator->() const { return ptr; }
     Node& operator++{ ptr = ptr->pnext; return *this; }//前置式++,返回的是引用
     Node operator++(int) { Node t = *this; ++*this; return t; }//后置式++,因ptr已经改变,返回的不是引用
     bool operator==(const Node& i){ return i.ptr == ptr; }
     bool operator!= (const Node& i){ return i.ptr != ptr; }//同样为了find函数中而重载!=符号
};

同样,我们在*first != value之间,我们需要重载!=算子(在find函数中是*first与value比较,而*first是T类型,这里T类型就是struct node类型):
bool operator==(const node& i, int value){ return i.value == value; }
bool operator!= (const node& i, int value){ return i.value != value; }

好了,现在我们可以使用以下代码使用我们的链表:
node *head,*end;
node *tmp = new node;
tmp->value = 100;
tmp->pnext = NULL;
head = end = tmp;

for(int i = 0; i < 10; ++i)
{
tmp = new node;
tmp->value = i+1;
tmp->pnext = NULL;
end->pnext = tmp;
end = tmp;
}
//以上代码生成了一个链表,现在看怎么运用我们的find函数:
Node<node> r;
r = find(Node<node>(head), Node<node>(), 5);
//Node<node>(head)调用Node<node>构造函数,入参是head
//同理,Node<node>()的入参是NULL
if( NULL != r ) cout<<(*r).value<<endl; //如果r不是NULL,就输出

到这里,我们学会了如何封装一个struct,使其能被find函数调用,很有成就感吧?感谢jjh吧。

我们重新审视find函数,发现find函数需要声明两个类型,一个是T,一个是I,其实T就是的*I,C++没有typeof算子,但是编译器有推导功能:

办法一:
template<class I,class T>
void fun_impl(I i, T v)
{
   //do some work
}

template<class I>
void fun(I i)
{
fun_impl(i, *i);//编译器通过*i推导出*i的类型,然后调用fun_impl完成功能
}
于是我们可以通过如下代码完成功能:
int i;
fun(&i);

     似乎解决了问题,但是问题不断,如果入参不是一般参数,而是一个函数的传回值,就不灵了。

方法二(嵌套类型声明,原文称“巢狀式的型別宣告”):
假设我们的Node模板类封装了类型为T节点
template<class T>
struct Node{
     typedef T value_type;//嵌套类型
      T *ptr;
     Node(const T* p):ptr(p){}
     T& operator*() const { return *ptr; }
     T* operator->() const { return ptr; }
    .....
};

那泛化函数可以如此声明:
template<class T>
typename Node<T>::value_type
func(Node<T>&it)//传入一个iterator
{
return *(it.ptr);
}

然后我们可以用下面的代码:
Node<int>ite(new int(100));
cout<<func(ite)<<endl;

这个函数的问题在于每个需要为每一种iterator写一个func,Node写一个,以后Stack也许也要写一个,有没有办法可以避免具体类型Node之类出现呢?当然有了(traits粉墨登场),traits是特性的意思,从众多iterator中“提取”type特性:

template<class Iterator>
struct iterator_traits
{
typedef typename Iterator::value_type value_type;//typename为了使编译通过,其实g++ 3.4.2下不会报错
};


至于原生指针,我们使用partial specialization
template<class T>
struct iterator_traits<T*>
{
typedef T value_type;
}
template<class T>
struct iterator_traits<const T*>
{
typedef T value_type;
}

于是乎,func函数可以写成如下:
template<class T>
typename iterator_traits<T>::value_type
func(T t)
{
return *(t.value);
}

//测试
int main( char argc, char *argv[] )
{
char *p[100];

Node<int>ite (new int(100));
std::cout<<func(ite)<<"\n";

Node<char>cite(new char('a'));
std::cout<<func(cite)<<"\n"; 

Node<char*>pstr(p);
  
return 0;

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多