栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++模板编程(12)---更深入的基础技术:友元(Friend)

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++模板编程(12)---更深入的基础技术:友元(Friend)

Friend 声明语句的基本概念很简单:指定某些classes或functions,让他们可以对friend声明语句所在的class进行特权存取。但是下面两个事实使这个概念复杂化了:

1)friend声明的语句可能是某一entity的唯一声明;

2)friend函数声明可以就是其定义。

friend class 声明语句不能成为一个定义式,这就大大降低了问题的发生。涉及template时,唯一需要考虑的是新增情况:你可以把某个class template的实例声明为friend:

template 

class Node;

template

class Tree{

        friend class  Node;

        ...

};

在class template的某一实体成为其它class或class template的friend之前,class template Node必须已经被声明可见。但对常规class来说,没有这个限制。

template

class Tree{

        friend class Factory; //OK,即使是Factory的首次声明

        friend class Node; // Error(如果没有声明过Node)

}

1.友元函数Friend Function

    Function template的具体实现实例可以成为别人的一个friend function。前提是该function template名称之后必须紧跟着以角括号括起的自变量列或者说实参列表,如果编译器可推导出所有自变量,自变量列表可以为空。

template

void combine(T1,T2);

class Mixer

{

        friend void combine<>(int&, int&); //OK, T1=int&, T2= int&

        friend void combine(int,int); //OK; T1 = int, T2 = int

        friend void combine(char, int); //OK,T1= char, T2 = int

        friend void combine(char& ,int); //Error, not match

        friend void combine<>(long,long) {...} //Error, 不能在此处定义函数

}

注意,我们无法定义一个template实体instance,最多只能定义一个特化体specailization。因此一个令某实体获得名称的friend声明语句,不能够是个定义式。

如果friend名称之后没有跟着角括号,有两种可能:

1)如果这不是个资格修饰名称(不含::),就绝不会引用某个template实体;

2)如果这是一个资格修饰名称(含有::),它就必定引用一个先前已经定义的function或function template。

以下例子有助于理解各种可能情况:

void multiply(void*) //ordinary function

template

void multiply(T);    //function template

class Comrades

{

        friend void multiply(int) {} //定义了一个新函数::multiply(int)

        friend void ::multiply(void*); //引用先前的常规函数

        friend void ::multiply(int);

        friend void ::multiply(double*); //修饰名称可以带角括号,但编译器必须见到模板

        friend void ::error() {} //Error:带修饰资格的friend,不能是个定义。

}

如果把friend声明于class templates中,先前所说的规则也全部适用,而且template parameter可参与到friend function之内:

template

class Node {

        Node* allocate();

...

};

template

class List {

        friend Node* Node::allocate();

        ...

};

然而把friend function定义于class template中可能会引发一个有趣的错误因为任何只在template中被声明的object,都是直到template被实例化后才能成为具体实体concrete entity。考虑下面例子:

template

class Creator {

        friend void appear() { //定义一个新函数

               ...

        }

};

Creator mirache;  //::appear()此时生成

Creator oops; //Error: 试图再次生成appear()

在这个例子中,两个不同的具体实现产生出两个完全相同的定义,违反ODR原则。

因此,我们必须确保class template的 template parameters出现在定义于该tamplate之内的所有friend functions的类型中。现在对先前的例子做些改动:

template

class Creator {

        friend void feed(Creator*) {

        ...

        }

};

Creator one; //产生:::feed(Creator*)

Creator two; //产生:::feed(Creator*)

这个例子中每个Creator都实例化产出一个不同的函数。注意,虽然这些函数是在template实例化过程中产生的,但它们还是常规函数,不是某个模板的实体。

2.友元模板Friend Templates

通常当我们定义一个friend,而它是个function或是个class template时,我们可以明确指定以哪一个物体entity作为friend。但有时候把一个template的所有实体都指定为某个class的friend也相当有用。这是经由一种所谓的friend template机制实现的。例如,

class Manager

{

        template

                friend class Task;

        template

                friend void Schedule::dispatch(Task*);

        template

                friend int ticket() {

                        return ++Manager::counter;

                }

        static int counter;

};

和常规friend声明语句一样,只有当friend template产生一个无修饰函数名(unqulified function name),而且该名称之后不紧跟角括号,这个friend template才可以是一个定义式。

Friend template 只能声明primary template及primary templates的成员。任何与该primary template相关的偏特化和明确特化体都将自动被编译器视为friends。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1000850.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号