跟我学C++中级篇——模板友元的应用
一、友元
友元在以前分析过,而且一般编程是不推荐使用友元的,原因是友元破坏了类的封装性。但凡事总有例外,在某些情况下,用友元还是比较方便的,那么该用还得用,不能因噎废食。普通的友元,各种书籍资料和网上多得很。一般来说,可以把友元分为两大类:
1、普通的友元
这种友元的使用,不管是类还是函数,开发者都相对比较熟悉,此处就不再赘述,如果有不明白的可以 翻一下书籍。一般来说,即使在模板里,使用特化的或者实例化的模板类或者函数,也看做是普通友元。模板只要特化或者说实例化就和普通类或者函数没有什么区别了。
2、模板的友元
这里重点分析一下模板中使用模板函数或者模板类做为友元时的处理情况,这里面涉及到一些具体的编译器的不同处理方式,需要引起重视。
二、模板函数做为友元
老规矩行先看代码:
#include <iostream>//第一种情况
template <typename T> class FriendF {
public:T t_;public:FriendF() { t_ = T(1.9); }// VS OK// template <typename T> friend void Display(const FriendF<T> &f);template <typename U> friend void Display(const FriendF<U> &f);
};template <typename U> void Display(const FriendF<U> &f) {std::cout << "cur value is:" << f.t_ << std::endl;
}int friendtest() {FriendF<int> f;Display<int>(f);return 0;
}
//
//第二种情况
template <typename T> class FriendF1;
template <typename T> void Display(const FriendF1<T> &a);template <typename T> class FriendF1 {T t_;public:FriendF1() { t_ = T(1.6); }friend void Display<T>(const FriendF1<T> &f);
};template <typename T> void Display(const FriendF1<T> &f) {std::cout << "cur value is:" << f.t_ << std::endl;
}int friendtest1() {FriendF1<int> f1;Display<int>(f1);return 0;
}
///
//第三种情况template <typename T> class FriendF2 {T t_;public:FriendF2() { t_ = T(2.0f); }friend void Display(const FriendF2<T> &f2) {std::cout << f2.t_ << std::endl;}
};int friendtest2() {FriendF2<int> f2;Display(f2);return 0;
}///
//第四种情况
template <typename T> class NData;
template <typename T> void swap1(NData<T> &n1, NData<T> &n2);
template <typename T> void Display1(NData<T> &n1, NData<T> &n2);
template <typename T> void swap(NData<T> &n1, NData<T> &n2);template <typename T> class NData {
public:NData() {}NData(T t) : t_(t) {}public:NData<T> *left_;NData<T> *right_;T t_;public:friend void swap1<>(NData<T> &, NData<T> &);friend void swap<>(NData<T> &, NData<T> &);friend void Display1<>(NData<T> &, NData<T> &);
};
template <typename T> void Display1(NData<T> &n1, NData<T> &n2) {std::cout << n1.t_ << std::endl;
}
template <typename T> void swap1(NData<T> &n1, NData<T> &n2) {std::swap(n1.left_, n2.left_);std::swap(n1.right_, n2.right_);std::swap(n1.t_, n2.t_);std::cout << "vlaue is:" << n1.t_ << std::endl;
}
template <typename T> void swap(NData<T> &n1, NData<T> &n2) {// // std::swap(n1.left_, n2.left_);// // std::swap(n1.right_, n2.right_);// std::swap(n1.t_, n2.t_);std::cout << "n1,b" << n1.t_ << std::endl;
}int friendtest3() {NData<int> nd1;NData<int> nd2;swap(nd1, nd2);swap1(nd1, nd2);Display1(nd1, nd2);return 0;
}
int main()
{friendtest();friendtest1();friendtest2();friendtest3();
}
在模板函数做为友元的情况下,分成了三种情况两大类:
1、模板类内声明,模板外实现
第一种情况就是这种,在类声明了模板函数为友元,但是在类外实现函数体。
2、模板类内声明,类内实现
第三种就是在类内直接实现模板函数友元的函数体。
3、模板类外声明
第二种情况就是把友元模板函数在类外进行声明,有点类似于头文件的作用,然后在类中声明友元,类外进行函数体的实现。第四种情况中,其实和第二种情况基本没有区别。只是用<>来表明这是一种模板函数的具现化。
*这里需要说明一下"friend void swap1<>(NData &, NData &)"这种<>尖括号声明,用侯捷老师的书中的原话是“使用空的template argument list,这种形式告诉编译器,只从template具现体中挑选合适的呼叫对象,所有template parameters 都自 call parameters 推导而得”。所以才在下面说第四种情况视情况而定的原因。
其实它的意思就是说,这个是要从具体的调用参数推导出来。也就是说,常规的函数是允许参数自动类型转换,但模板参数推断时不允许,这就等于在实际推导时要根据已经生成的挑选(推导)最匹配的。
两大类是从另外一种角度看:
1、可以直接在类内声明限定约束(bound)模板友元的参数类型
也就是说,友元模板参数要和应用 其类的模板参数保持一致。第一种情况的注释版第二种及第三种情况都是约束的情况。这种也容易为开发者理解和应用。
2、模板未对友元模板进行约束(unbound)
这种情况就是第一种情况,第一种情况注释的部分在vs上可以 编译应用 ,但在Gcc和Clang上编译会出现“Declaration of ‘T’ shadows template parameter”,这个在前面也分析过(“模板的模板参数的匹配”)。
第四种情况视情况而定吧。
三、模板类做为友元
而模板类做为友元可以 分为两种情况:
//第一种情况
template <typename T> class FriendB;template <typename T> class FriendA {
public:T t_;public:FriendA() { t_ = T(3.6); }friend class FriendB<T>;
};
//第二种情况
template <typename T> class FriendAA {
public:T t_;public:FriendAA() { t_ = T(100); }template <class U> friend class FriendBB;
};
template <typename U> class FriendBB {
public:static void Display(const FriendAA<U> &faa) {std::cout << "cur value is:" << faa.t_ << std::endl;}
};
template <typename T> class FriendB {
public:static void Display(const FriendA<T> &fa) {std::cout << "cur value is::" << fa.t_ << std::endl;}
};int friendclass() {FriendA<int> aa1;FriendAA<double> aa2;FriendB<int>::Display(aa1);FriendBB<double>::Display(aa2);return 0;
}
int main()
{
friendclass();
}
它们两种情况都是在类声明为友元,但是第一种情况中在前声明友元前进行了友元模板类的前向声明;第二种情况则直接在类内声明。前者更类似于一种实例化的类,在前面的学习中知道,在模板类中,声明的模板形参可以在内部认为已经实例化,直接使用。而第二种则和前面函数模板类似,属于不受约束的方式,是一种声明形式。
也就是说,第一种是码元模板参数受约束的形式,而第二种则为不受约束的形式。
四、问题
在这次的例程编写中,遇到了一个基础问题,觉得对初学者甚至中等水平开发者还是有一定意义的,这里展示出来。
......template <typename T> class NData {
public:NData() {}NData(T t) : t_(t) {}public:NData<T> *left_;NData<T> *right_;T t_;public:friend void swap1<>(NData<T> &, NData<T> &);friend void swap<>(NData<T> &, NData<T> &);
......
};template <typename T> void swap1(NData<T> &n1, NData<T> &n2) {std::swap(n1.left_, n2.left_);std::swap(n1.right_, n2.right_);std::swap(n1.t_, n2.t_);std::cout << "vlaue is:" << n1.t_ << std::endl;
}
template <typename T> void swap(NData<T> &n1, NData<T> &n2) {// // std::swap(n1.left_, n2.left_);// // std::swap(n1.right_, n2.right_);// std::swap(n1.t_, n2.t_);std::cout << "n1,b" << n1.t_ << std::endl;
}
也就是函数模板做为友元的第四种情况中,使用swap做函数名时,总是报"assertion failed: template argument must be a complete class or an unbounded array"。有点摸不着头脑,类NData是一个完全定义好的啊。后来突然想到会不会是名空间污染的问题(std::swap())。于是又写了一个swap1,结果没问题。验证与想的完全一致,回头查看一下代码,在工程中被包含的cpp文件中声明了using namespae std,所以在实际工程中,尽量增加自定义的名字空间,并且不要使用类似“using namespae std”这种偷懒的方式来编程。
五、总结
C++语言更需要多实践,多干活自然就会有各种情况遇到,和所学的知识不断的验证、巩固。查遗补缺,技术的增长就会越来越快,光死记理论,时间长了要么忘记了,要么记混了,写起代码来还是有无法驾驭的情况。
相关文章:
跟我学C++中级篇——模板友元的应用
一、友元 友元在以前分析过,而且一般编程是不推荐使用友元的,原因是友元破坏了类的封装性。但凡事总有例外,在某些情况下,用友元还是比较方便的,那么该用还得用,不能因噎废食。普通的友元,各种…...

软件测试基础篇——MySQL
MySQL 1、数据库技术概述 数据库database:存放和管理各种数据的仓库,操作的对象主要是【数据data】,科学的组织和存储数据,高效的获取和处理数据SQL:结构化查询语言,专为**关系型数据库而建立的操作语言&…...

FreeRTOS(二值信号量)
资料来源于硬件家园:资料汇总 - FreeRTOS实时操作系统课程(多任务管理) 目录 一、信号量的概念 1、信号量的基本概念 2、信号量的分类 二、二值信号量的定义与应用 1、二值信号量的定义 2、二值信号量的应用 三、二值信号量的运作机制 1、FreeRTOS任务间二值…...
leetcode面试题:动物收容所(考查对队列的理解和运用)
题目: 有家动物收容所只收容狗与猫,且严格遵守“先进先出”的原则。在收养该收容所的动物时,收养人只能收养所有动物中“最老”(由其进入收容所的时间长短而定)的动物,或者可以挑选猫或狗(同时…...

【Linux命令行与Shell脚本编程】第十八章 文本处理与编辑器基础
Linux命令行与Shell脚本编程 第十八章 文本处理与编辑器基础 文章目录 Linux命令行与Shell脚本编程第十八章 文本处理与编辑器基础 文本处理与编辑器基础8.1.文本处理8.1.1.sed编辑器8.1.1.1.在命令行中定义编辑器命令8.1.1.2.在命令行中使用多个编辑器命令8.1.1.3.从文件中读…...

2023牛客暑期多校训练营7
Beautiful Sequence 贪心,二进制,构造 Cyperation 模拟 ,数学 We Love Strings 分块,二进制枚举,二进制容斥dp Writing Books 签到 根据相邻两个异或值B,因为前小于等于后,故从高到低遍历B的每一…...

centos7升级glibc2.28
1 概述 centos7自带的glibc对于某些软件是太旧的,决定将glibc升级至2.28。 2 安装过程 2.1 下载glibc源码 mkdir -p /opt/third-party && cd /opt/third-party wget http://ftp.gnu.org/gnu/glibc/glibc-2.28.tar.gz tar -xf glibc-2.28.tar.gz cd glibc…...

腾讯云香港服务器租用_2核2G20M_2核4G30M
腾讯云香港服务器租用费用表,目前中国香港地域轻量应用服务器可选配置2核2G20M、2核2G30M、2核4G30M,操作系统可选Windows和Linux,不只是香港云服务器,新加坡、硅谷、法兰克福和东京服务器均有活动,腾讯云服务器网分享…...

十三、ESP32PS2摇杆(ADC)
1. 运行效果 在上下左右操作PS2摇杆的时候,会检测到数据 2. 滑动电阻...

网络安全的相关知识点
网络安全威胁类型: 1.窃听:广播式网络系统。 2.假冒 3.重放:重复一份报文或者报文的一部分,以便产生一个被授权的效果。 4.流量分析 5.数据完整性破坏 6.拒绝服务 7.资源的非授权使用 8.陷门和特洛伊木马:木马病毒有客…...
算法练习(6):牛客在线编程06 递归/回溯
package jz.bm;import java.io.PushbackInputStream; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays;public class bm6 {/*** BM55 没有重复项数字的全排列*/ArrayList<ArrayList<Integer>> res new ArrayList<>()…...
C#使用OpenCv(OpenCVSharp)图像局部二值化处理实例
本文实例演示C#语言中如何使用OpenCv(OpenCVSharp)对图像进行局部二值化处理。 目录 图像二值化原理 局部二值化 自适应阈值 实例 效果...

MySQL多表关联查询
目录 1. inner join: 2. left join: 3. right join: 4.自连接 5.交叉连接: 6、联合查询 7、子查询 1. inner join: 代表选择的是两个表的交差部分。 内连接就是表间的主键与外键相连,只取得键值一致…...
flutter开发实战-CustomClipper裁剪长图帧动画效果
flutter开发实战-CustomClipper裁剪长图帧动画效果 在开发过程中,经常遇到帧动画的每一帧图显示在超长图上,需要处理这种帧动画效果。我这里使用的是CustomClipper 一、CustomClipper CustomClipper继承于Listenable abstract class CustomClipper e…...

CSS 中的优先级规则是怎样的?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐内联样式(Inline Styles)⭐ID 选择器(ID Selectors)⭐类选择器、属性选择器和伪类选择器(Class, Attribute, and Pseudo-class Selectors)⭐元素选择器和伪元素选择器…...

概率图模型(Probabilistic Graphical Model,PGM)
概率图模型(Probabilistic Graphical Model,PGM),是一种用图结构来描述多元随机变量之间条件独立性的概率模型。它可以用来表示复杂的概率分布,进行有效的推理和学习,以及解决各种实际问题,如图…...

Oracle 知识篇+会话级全局临时表在不同连接模式中的表现
标签:会话级临时表、全局临时表、幻读释义:Oracle 全局临时表又叫GTT ★ 结论 ✔ 专用服务器模式:不同应用会话只能访问自己的数据 ✔ 共享服务器模式:不同应用会话只能访问自己的数据 ✔ 数据库驻留连接池模式:不同应…...

MySQL 数据库文件的导入导出
目录 数据库的导出 导出整个数据库 导出数据库中的数据表 导出数据库结构 导出数据库中表的表结构 导出多个数据库 导出所有数据库 数据库的导入 数据库的导出 mysqldump -h IP地址 -P 端口 -u 用户名 -p 数据库名 > 导出的文件名 用管理员权限打开cmd进入MySQL的bi…...

找不到资产文件project.assets.json
NuGet 在“obj”文件夹中写入名为 project.assets.json 的文件,.NET SDK 使用该文件来获取有关要传递到编译器的包的信息 。 如果在生成过程中找不到资产文件 project.assets.json,则会发生此错误。 1.执行命令的方式解决 点击工具,分别展开命…...

【python】python将json字符串导出excel | pandas处理json字符串保存为csv
如何将json转为csv 1、通过json直接转为csv 在Python中,你可以使用pandas库来处理DataFrame(数据帧)和将JSON数据转换为CSV格式。下面是一个简单的示例代码,展示了如何使用pandas库将JSON数据转换为CSV文件: import p…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...

以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...