C++——详解类模板与友元函数

这篇具有很好参考价值的文章主要介绍了C++——详解类模板与友元函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

C++——详解类模板与友元函数
纵有疾风起,人生不言弃。本文篇幅较长,如有错误请不吝赐教,感谢支持。

类模板与友元函数

模板类的友元函数有三类:

  • 1)非模板友元函数:友元函数不是模板函数,而是利用模板类参数生成的函数。
  • 2)约束模板友元函数:模板类实例化时,每个实例化的类对应一个友元函数。
  • 3)非约束模板友元函数:模板类实例化时,如果实例化了n个类,也会实例化n个友元函数,每个实例化的类都拥有n个友元函数。

1️⃣非模板友元函数

非模板友元函数就是将一个普通函数声明为友元函数。我们先来看一段代码:

#include <iostream> 
#include<string> 
using namespace std;
template<class T1,class T2>
class Maker
{
private:
    T1 m_x;
    T2 m_y;
public:
    Maker(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};
Maker<string,int> m1("感谢支持强风吹拂king的博客",666);
void show()
{
    cout << "x = " << m1.m_x << ", y = " << m1.m_y << endl;
}
int main()
{
    show();
}

📝解释:

Maker是模板类,有两个模板参数T1,T2,在模板Maker类中用T1和T2分别定义了m_x,和m_y,构造函数使用初始化列表给m_x,和m_y赋值。
用模板类Maker定义全局对象m1,再定义全局函数show(),在全局函数show()中访问模板类Maker的私有成员,看下面运行结果(报错):
C++——详解类模板与友元函数
如果我们把全局函数show(),给声明成模板Maker的友元函数可以访问Maker的私有成员吗?

friend void show();

🖲执行结果如下:
C++——详解类模板与友元函数

在类模板Maker中,将普通函数show()函数声明为友元函数,则show()函数是类模板Maker所有实例的友元函数。例如,它是Maker<string,int>,Maker<string,int>,Maker<string,double>等的友元函数。

这种为类模板设置友元函数的方法在语法上没有任何错误,但是要用模板类去创建全局对象,代码这么写实在是太笨了,在学习友元的时候,如果Maker是普通类,我们可以把Maker普通类的引用传给友元函数而不必创建全局对象
那么Maker是类模板,可以这么写吗?答案是不可以。Maker只是类的通用描述,根本不存在类名叫Maker的类,但是你具体化函数模板,也就是后面加上参数列表<具体的数据类型>,这就可以当做一个具体的类,就可以像普通类一样使用。

#include <iostream> 
#include<string> 
using namespace std;
template<class T1,class T2>
class Maker
{
private:
    T1 m_x;
    T2 m_y;
public:
    Maker(const T1 x, const T2 y) : m_x(x), m_y(y) {}
    friend void show(const Maker<string, int>& a);
};
void show(const Maker<string, int>& a)
{
   cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
}
int main()
{
    Maker<string,int> m1("感谢支持强风吹拂king的博客",777);
    show(m1);
    return 0;
}

🖲执行结果如下:
C++——详解类模板与友元函数

如果main函数内部我们这样写,编译器会报错吗?

int main()
{
    Maker<char,int> m1('k',666);
    show(m1);
    return 0;
}

错误如下:
C++——详解类模板与友元函数那么我们重载show函数,为Maker<char,int> m1(“k”,666);单独写一个show函数,并声明为Maker的友元函数。

void show(const Maker<char, int>& a)
{
   cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
}

🖲运行结果如下:
C++——详解类模板与友元函数

编译通过了,但是问题来了,现在这种方法解决友元函数的问题,但是很麻烦,要为模板类的每一个实例化版本都要写一个友元函数,所以,C++提供了一个解决方案,利用模板参数,自动生成友元函数,这个友元函数也叫作非模板友元。
非模板友元:友元函数不是模板函数,而是利用模板类参数生成的函数,只能在类内实现。

#include <iostream> 
#include<string> 
using namespace std;
template<class T1, class T2>
class Maker
{
private:
    T1 m_x;
    T2 m_y;
public:
    Maker(const T1 x, const T2 y) : m_x(x), m_y(y) {}
    //友元函数的形参用模板的通用类型
    friend void show(const Maker<T1, T2>& a)
    {
        cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
    }
};
int main()
{
    Maker<string, int> m1("感谢支持强风吹拂king的博客", 666);
    show(m1);
    Maker<char, int> m2('k', 666);
    show(m2);
    return 0;
}

🖲运行结果如下:
C++——详解类模板与友元函数

这种方法的本质是编译器利用模板参数为我们生成友元函数,但有一点我们要注意⚠️:
编译器利用模板参数生成友元函数,但是这个友元函数不是函数模板。

📝我举个例子证明“友元函数不是函数模板”一下:

#include <iostream> 
#include<string> 
using namespace std;
template<class T1, class T2>
class Maker
{
private:
    T1 m_x;
    T2 m_y;
public:
    Maker(const T1 x, const T2 y) : m_x(x), m_y(y) {}
    //友元函数的形参用模板的通用类型
    friend void show(const Maker<T1, T2>& a)
    {
        cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
    }
    friend void show(const Maker<string, int>& a);
    friend void show(const Maker<char, int>& a);
};
void show(const Maker<char, int>& a)
{
    cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
}
void show(const Maker<string, int>& a)
{
    cout << "x = " << a.m_x << ", y = " << a.m_y << endl;
}
int main()
{
    Maker<string, int> m1("感谢支持强风吹拂king的博客", 666);
    show(m1);
    Maker<char, int> m2('k', 666);
    show(m2);
    return 0;
}

这两个有具体类型的函数和模板参数的函数,它们之间是什么关系?
如果上面带模板参数的是函数模板,那两个带具体类型的就是函数模板的具体化版本。在模板技术中,普通函数,具体化版本和函数模板是可以共存的,编译器会优先使用普通函数和具体化版本,现在这三个函数之间的关系也是这样的吗?
C++——详解类模板与友元函数
C++——详解类模板与友元函数

函数出现了重载,为什么编译器不是优先选择一个函数调用,而是报重定义的错误呢?原因很简单,void show(const Maker<T1, T2>& a)不是函数模板,只是借用模板参数,编译器创建模板类实例的时候,会用具体的数据类型去替代模板参数,生成友元函数的实体,与下面定义的具体类型的函数发生代码冲突。报重定义的错误。

💬小结一下:
从上面的程序中我们可以看出,非模板友元的使用特别得呆板。

  • 如果我们想为某种具体化的类模板单独写友元函数,这是做不到的,会报重定义的错误。(例如给Maker<string,int>单独写个友元函数,是不行🙅🏻‍♂️的)
  • 非模板友元函数,在哪个模板类定义,就只能用于这个类,不能用于其他类,因为非模板友元借助的是本类模板参数,并且还只能在本类的类内定义。

2️⃣约束模板友元函数

约束模板友元函数是将一个函数模板声明为类的友元函数。函数模板的实例化类型取决于类模板被实例化时的类型,类模板实例化时会产生与之匹配的具体化友元函数。

📝使用约束模板友元的步骤:

  • 第一步,在模板类的定义前,声明友元函数模板,为的是让模板类知道友元函数模板的存在。
  • 第二步再次声明友元函数模板,在类模板中将函数模板声明为友元函数。在声明友元函数时,函数模板要实现具体化,即函数模板的模板参数要与类模板的模板参数保持一致,目的是让编译器知道需要实例化的友元函数模板,类模板和函数模板本来没什么关系的,正是第二步让它们产生了关系,编译器在实例化某种数据类型的类模板时,也会实例化这种数据类型的模板函数。
  • 第三步,友元函数模板的定义,放在类模板下,因为友元函数是函数模板,所以可以和具体化的函数模板共存,不会发生代码冲突,报重定义的错误。
#include <iostream>
#include<string>
using namespace std;
// 约束模板友元:模板类实例化时,每个实例化的类对应一个友元函数。
template <typename T>
void show(T& a); // 第一步:在模板类的定义前面,声明友元函数模板。
template<class T1, class T2>
class AA // 模板类AA。
{
    friend void show<>(AA<T1, T2>& a); // 第二步:在模板类中,再次声明友元函数模板。
    T1 m_x;
    T2 m_y;
public:
    AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};
template <typename T> // 第三步:友元函数模板的定义。
void show(T& a)
{
	cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
}
template <> // 第三步:具体化版本。
void show(AA<int, string>& a)
{
	cout << "具体AA<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}
int main()
{
    AA<int, string> a1(66, "感谢支持强风吹拂king的博客");
    show(a1); // 将使用具体化的版本。
    AA<char, string> a2(66, "感谢支持强风吹拂king的博客");
    show(a2);// 将使用通用的版本。
}

🖲运行结果如下:
C++——详解类模板与友元函数
解释:
第一次在声明时,show()函数与AA类模板没有关系,只是让编译器知道函数模板的存在,第二步,函数模板再次声明,并完成显示实例化,模板参数变为了AA类,当生成AA< char,string>模板类时,会生成与之匹配的show< char,string>()函数和作为友元函数。
这种友元函数模板可以应用于多个模板类,只是第二步,函数模板在类内具体化不一样。
把类模板AA的代码复制一遍,将类名改为BB。
📝代码举例:

#include <iostream> // 包含头文件
#include<string>
using namespace std;
// 约束模板友元:模板类实例化时,每个实例化的类对应一个友元函数。
template <typename T>
void show(T& a); // 第一步:在模板类的定义前面,声明友元函数模板。
template<class T1, class T2>
class AA // 模板类AA。
{
	friend void show<>(AA<T1, T2>& a); // 第二步:在模板类中,再次声明友元函数模板。
	T1 m_x;
	T2 m_y;
public:
	AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};
template<class T1, class T2>
class BB // 模板类BB。
{
	friend void show<>(BB<T1, T2>& a); // 第二步:在模板类中,再次声明友元函数模板。
	T1 m_x;
	T2 m_y;
public:
	BB(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};
template <typename T> // 第三步:友元函数模板的定义。
void show(T& a)
{
	cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
}
template <> // 第三步:具体化版本。
void show(AA<int, string>& a)
{
	cout << "具体AA<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}
template <> // 第三步:具体化版本。
void show(BB<int, string>& a)
{
	cout << "具体BB<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}
int main()
{
	AA<int, string> a1(66, "感谢支持强风吹拂king的博客");
	show(a1); // 将使用具体化的版本。
	AA<char, string> a2(66, "感谢支持强风吹拂king的博客");
	show(a2); // 将使用通用的版本。
	BB<int, string> b1(66, "感谢支持强风吹拂king的博客");
	show(b1); // 将使用具体化的版本。
	BB<char, string> b2(66, "感谢支持强风吹拂king的博客");
	show(b2); // 将使用通用的版本。
}

🖲运行结果如下:
C++——详解类模板与友元函数
关于友元函数的声明,多说一点:

template<typename U>//类模板的定义 
class A 
{
  ……//其他成员
  friend void func<U>();//声明无参友元函数func() 
  friend void show<>(A<U>& a);//声明有参友元函数show()
  ……//其他成员
  };

在上述代码中,将函数模板func()与show()声明为类的友元函数
func()是无参的函数模板,我们在学习函数模板的时候,必须显示或隐式的让编译器知道模板参数将要被哪个具体的数据类型替换,以便生成具体的函数定义,但这是无参的函数模板,隐式实例化不行(没参数),所以只能借助参数列表显示实例化。
show()函数模板有一个模板类参数,编译器可以根据函数参数隐式推导出模板参数,生成函数定义,因此show()函数模板具体化中<>可以为空。

💬小结一下:
两个要点:

  • 可以某种具体化的类模板单独写友元函数,例如上面代码中为AA<int, string> a1(66, “感谢支持强风吹拂king的博客”);提供单独具体化的友元函数
  • 可以支持多个类模板。

这种友元方案更有价值,就是语法稍微麻烦一点。

3️⃣非约束模板友元函数

模板类实例化时,如果实例化了n个类,也会实例化n个友元函数,每个实例化的类都拥有n个友元函数。
这其实不科学,按理说每个实例化的类只需要一个友元函数即可,不需要n个,其他数据类型的友元函数和自己没关系,其实原因就是没有约束模板友元函数的第二步,没有将函数模板具体化,函数模板和类模板没有绑定起来。
声明非约束模板友元函数示例代码如下所示:
C++——详解类模板与友元函数
📝案例:

#include <iostream> // 包含头文件。
using namespace std; 
// 非类模板约束的友元函数,实例化后,每个函数都是每个每个类的友元。
template<class T1, class T2>
class AA
{
	template <typename T> friend void show(T& a); // 把函数模板设置为友元。
	T1 m_x;
	T2 m_y;
public:
	AA(const T1 x, const T2 y) : m_x(x), m_y(y) { }
};
template <typename T> void show(T& a) // 通用的函数模板。
{
	cout << "通用:x = " << a.m_x << ", y = " << a.m_y << endl;
}
template <>void show(AA<int, string>& a) // 函数模板的具体版本。
{
	cout << "具体<int, string>:x = " << a.m_x << ", y = " << a.m_y << endl;
}
int main()
{
	AA<int, string> a1(66, "感谢支持强风吹拂king的博客");
	show(a1); // 将使用具体化的版本。
	AA<char, string> a2(66, "感谢支持强风吹拂king的博客");
	show(a2);// 将使用通用的版本。
}

🖲运行结果如下:
C++——详解类模板与友元函数
虽然说运行结果和约束模板友元函数一致,但还是推荐使用约束模板友元函数。文章来源地址https://www.toymoban.com/news/detail-502163.html

到了这里,关于C++——详解类模板与友元函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SYSU程设c++(第九周)函数对象、友元函数、友元类

    函数对象:         如果一个类 定义了operator()运算符函数 ,则 可以使用该类的对象名为函数名 调用这个函数.          函数对象是一个对象, 但 调用形式和普通函数调用一样 ,因此取名叫函数对象 (注意operator()先有个括号,接着才是括号(参数列表)) 友元函数:  f

    2023年04月23日
    浏览(26)
  • C++核心编程——详解函数模板

    纵有疾风起,人生不言弃。本文篇幅较长,如有错误请不吝赐教,感谢支持。 ①为什么有模板? 在C++程序中,声明变量、函数、对象等实体时,程序设计者需要指定数据类型,让编译器在程序运行之前进行类型检查并分配内存,以提高程序运行的安全性和效率。 但是这种强

    2024年02月09日
    浏览(28)
  • C++友元函数

    友元——让函数或者类作为另外一个类的朋友,则可以访问当前类的private或者protected 友元friend机制允许一个类授权其他的函数访问它的非公有成员 友元声明以friend开头,它只能出现在类的声明中,它们不受其在类体中的public、private和protected区的影响 1、外部函数友元

    2024年02月06日
    浏览(35)
  • 波奇学C++:友元函数,友元类,内部类,匿名对象,优化构造

    这个代码会报错,原因是_a是a._a的私有成员,fun()是类外函数不能访问。 加上友元就可以突破限制 关于友元的知识 友元函数是一种声明,表明这个函数可以访问私有变量。 友元函数本质上不属于类函数,所以这个声明是不受访问限定符限制的,可以定义在public或者private中。

    2024年02月05日
    浏览(27)
  • C++之类的友元函数

    类的友元函数是指在类中声明为友元的函数。友元函数可以访问类的所有成员(包括私有成员),但它本身并不是类的成员函数,因此不需要通过类的对象来调用。友元函数主要用于在类的内部实现对其他类成员的访问和操作。 举例说明: 这个友元函数的参数是一个类A的对象引

    2024年01月22日
    浏览(27)
  • c++ 友元 运算符重载详解

    c++是面向对象的,目的之一:封装 封装: 优点之一,就是安全。 缺点:在某些特殊的场合,不是很方便。 华为与IBM 40亿的咨询故事 IBM需要对华为各级部门做深度咨询分析, 为了提高咨询效率,由任正非直接授权,直接获取各部门的所有权限。 使用前提: 某个类需要实现

    2024年02月11日
    浏览(31)
  • C++类和对象终章——友元函数 | 友元类 | 内部类 | 匿名对象 | 关于拷贝对象时一些编译器优化

    🌸作者简介: 花想云 ,在读本科生一枚,致力于 C/C++、Linux 学习。 🌸 本文收录于 C++系列 ,本专栏主要内容为 C++ 初阶、C++ 进阶、STL 详解等,专为大学生打造全套 C++ 学习教程,持续更新! 🌸 相关专栏推荐: C语言初阶系列 、 C语言进阶系列 、 数据结构与算法 、 Linu

    2023年04月15日
    浏览(30)
  • 【C++】类和对象(下篇)--->再识构造函数,static成员,友元,内部类,匿名对象

    目录 一、构造函数  1、构造函数体赋值  2、初始化列表  3、explicit 二、Static 成员  1、概念  2、特性 三、友元  1、友元函数  2、友元类 四、内部类  1、概念  2、特性 五、匿名对象 六、拷贝对象时的一些编译器优化 在创建对象时,编译器通过调用构造函数,给对

    2024年02月13日
    浏览(30)
  • c++类开发的第三篇(讲明白友元函数和this指针)

    c++实现了 封装 , 数据 和 处理数据的操作(函数) 是分开存储的。 c++中的 非静态数据成员 直接内含在类对象中,就像c语言的struct一样。 成员函数并不会出现在对象中,而是作为类的一部分存储在代码段中,需要通过对象或对象指针进行调用。成员函数可以访问类的所有成员

    2024年02月21日
    浏览(35)
  • [C++]类和对象,explicit,static,友元,构造函数——喵喵要吃C嘎嘎4

    希望你开心,希望你健康,希望你幸福,希望你点赞! 最后的最后,关注喵,关注喵,关注喵,大大会看到更多有趣的博客哦!!! 喵喵喵,你对我真的很重要! 佬佬催更,那就更吧!嗯~ o(* ̄▽ ̄*)o! 在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个

    2024年03月15日
    浏览(32)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包