万星星@豌豆荚 欢迎加入我们
一个吃软饭的男人!!!!!我只想写程序####
微博:http://weibo.com/wanlianwen
posts - 172,  comments - 1253,  trackbacks - 0
独自一人苦学模板,实在郁闷,翻译e文逼迫自己学习:(全部写完后整理)

8.4 友元
声明友元的思想其实很简单:标识一个类或者函数,对于将其声明为友元所在的类具有某种特权。但是在以下两个方面,问题似乎有点复杂:
1、一个友元声明只能是一个实体的唯一声明(注:我还没理解,不能解释)
2、友元函数声明的同时可以定义。

友元类在声明的时候不允许定义,所以一般不会有问题。在模板情况下,唯一一点不同就是可以将类模板的一个特定实例声明为友元:
template<typename T>
class Node;

template
<typename T>
class Tree{
    friend 
class Node<T>;
    …
}
;
注意:类模板在被一个类或者类模板声明为友元的时候必须是可见的,而对于普通的类则没有这种需求:
template<typename T>
class Tree
    friend 
class Factory;        // OK, even if first declaration of Factory
    friend class class Node<T>;  // ERROR if Node isn't visible
}

8.4.1 友元函数
函数模板的实例可以通过在函数名后面跟一对<>将其声明为友元。<>里面可以包含模板参数,如果编译器可以推导出模板参数,可以省略调模板参数:
template<typename T1, typename T2>
void combine(T1, T2);

class Mixer{
    friend 
void combine<>(int&int&);
    
// OK: T1 = int&, T2 = int&
    friend void combine<intint>(intint);
    
// OK: T1 = int, T2 = int
    friend void combine<char>(charint);
    
// OK: T1 = char T2 = int
    friend void combine<char>(char&int);
    
// ERROR: doesn't match combine() template
    friend void combine<>(longlong{ }
    
// ERROR: definition not allowed!
}
;
(注:在GCC下测试,VS2003不行)
注意:我们可以对模板进行专门化定义,但是不能对模板实例进行定义,所以声明模板实例为友元时不能定义。

如果名字后面跟有<>,有2种可能:
1、如果名字没有限定词(也就是前面没有::),就不可能是模板实例。如果在声明友元的地方没有非模板函数匹配的话,就是函数的首次声明,声明同时可以定义。
2、如果名字有限定词,那么一定与前面声明的函数或者模板函数有关,在匹配的时候优先选择非函数模板而不是函数模板,然而在声明的时候是不允许定义的。

下面的例子总结了各种可能性:
void multiply (void*);   // ordinary function 

template 
<typename T> 
void multiply(T);        // function template 

class Comrades 
    friend 
void multiply(int{} 
                         
// defines a new function ::multiply(int) 

    friend 
void ::multiply(void*); 
                         
// refers to the ordinary function above; 
                         
// not to the multiply<void*> instance 

    friend 
void ::multiply(int); 
                         
// refers to an instance of the template 

    friend 
void ::multiply<double*>(double*); 
                         
// qualified names can also have angle brackets 
                         
// but a template must be visible. 

    friend 
void ::error() {} 
                         
// ERROR: a qualified friend cannot be a definition 
}

(注:在VS2003和规则复合,GCC上编译完全通过)

前面例子我们讨论了一般类(相对于类模板)里面的友元函数,遵守的规则同样适用于类模板,但模板参数在确定友元函数的时候会起作用:
template<typename T>
class Node {
    Node
<T>* allocate();
}


template
<typename T>
class List {
    friend Node
<T>* Node<T>::allocate();
}
;

然而,当友元函数在类模板里面声明并定义的时候会出现有趣的现象,因为模板里面任何东西在未实例化之前都只是声明而没有具现。考虑下面的例子:
template<typename T>
class Creator 
    friend 
void appear() {  // a new function ::appear(), but it doesn't
        …                   // exist until Creator is instantiated
    }

}
;

Creator
<void> miracle;  // ::appear() is created at this point
Creator<double> oops;   // ERROR: ::appear() is created a second time!
这个例子中模板的两次实例化导致同一函数定义多次,违反了一次定义规则。

所以对于上面的情况,我们必须确保类模板的模板参数出现在声明并定义的友元函数形参里面(除非想阻止多次实例化一个类模板,这好像不太可能)。把这个规律运用到前面那个例子:
template<typename T>
class Creator 
    friend 
void feed(Creator<T>*){  // every T generates a different
                                    
// function ::feed()
    }

}
;

Creator
<void> one;     // generates ::feed(Creator<void>*)
Creator<double> two;   // generates ::feed(Creator<double>*)
这个例子中,Creator每个实例都生成不同的函数。注意这些函数是类模板实例产生的,但函数本身并不是函数模板的实例,而是普通函数。
还要注意这些函数体定义于类中,默认是内联的,所以在两个翻译单元产生相同的函数是没有问题的。(注:由于内联函数基于代码替代技术,所以一个函数定义所在的h文件被多次包含不会有问题,内联函数在编译器看来就是一段代码。)

8.4.2 友元模板
一般情况我们都是把函数模板或者类模板的某一实例声明为友元,但把模板本身声明为友元有时候也是非常有用的。这就需要所谓的模板友元,例如:
class Manager 
    template
<typename T>
    friend 
class Task;
    
    template
<typename T>
    friend 
void Schedule<T>::dispatch(Task<T>*);
    
    template
<typename T>
    friend 
int ticket() {
        
return ++Manager::counter;
    }
 
    
static int counter;
}
;
和普通友元函数一样,没有限定词且名字后面没有<>的函数模板在声明为友元同时可以定义。
声明原始函数模板或者原始类模板为友元的时候,所有相关的偏特化以及显示特例化都自动成为友元。
(注:测试代码
class Manager 
    template
<typename T>    // firend template
    friend class Task;
private:
    
int    _dat;
}
;

template
<typename T>
class Task {
}
;

template
<>
class Task<int> {            // specialization
public:
    
void accessData()
    
{
        _manager._dat 
= 0;    // can access Manager's private data
    }

private:
    Manager    _manager;
}
;


(本节完,汗...... 自己收获不小)
posted on 2006-04-06 23:48 万连文 阅读(949) 评论(0)  编辑 收藏 引用 所属分类: 模板

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


简历下载
联系我

<2006年4月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

常用链接

留言簿(66)

随笔分类

随笔档案

相册

搜索

  •  

最新评论

阅读排行榜

评论排行榜