跟我学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…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...

UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...