【C++】泛型编程 ⑩ ( 类模板的运算符重载 - 函数实现 写在类外部的同一个 cpp 代码中 | 类模板 的 外部友元函数二次编译问题 )

文章目录

  • 一、类模板 - 函数声明与函数实现分离
    • 1、类模板 外部 实现 构造函数
    • 2、类模板 外部 实现 普通函数
    • 3、类模板 外部 实现 友元函数
        • ( 1 ) 错误示例及分析 - 类模板 的 外部友元函数 二次编译 问题
        • ( 2 ) 正确写法
  • 二、代码示例 - 函数声明与函数实现分离
    • 1、代码示例
    • 2、执行结果


将 类模板 函数声明 与 函数实现 分开进行编码 , 有 三种 方式 :

  • 类模板 的 函数声明 与 函数实现 都写在同一个类中 , 也就是没有分开进行编码 ;
  • 类模板 的 函数实现 在 类外部进行 , 函数声明 和 实现 写在相同的 .cpp 源码文件中 ;
  • 类模板 的 函数实现 在 类外部进行 , 函数声明 和 实现 写在不同的 .h 和 .cpp 源码文件中 ;

上一篇博客 【C++】泛型编程 ⑨ ( 类模板的运算符重载 - 函数声明 和 函数实现 写在同一个类中 | 类模板 的 外部友元函数问题 ) 实现了第一种情况 , 类模板 的 函数声明 与 函数实现 都写在同一个类中 , 也就是没有分开进行编码 ;

本篇博客 , 开始分析 第二种情况 , 类模板 的 函数实现 在 类外部进行 , 写在相同的 .h 和 .cpp 源码文件中 ;





一、类模板 - 函数声明与函数实现分离




1、类模板 外部 实现 构造函数


原来的构造函数是 :

template <typename T>
class Student
{
public:
	Student(T x, T y)
	{
		this->a = x;
		this->b = y;
	}
}

如果将 构造函数 实现 , 写在类外部的 .cpp 源码中 ,

  • 首先 , 需要 声明 模板类型 , template <typename T> ;
  • 然后 , 通过 域操作符 访问 构造函数 , 并实现该函数 , 使用域操作符 时 , 前面的类 需要指定 具体的泛型类型 , 这里使用 声明的 T 模板类型 作为 具体的 泛型类型 ;
template <typename T>
Student<T>::Student(T x, T y)
{
	this->a = x;
	this->b = y;
}

在 类模板 内部 , 只需要声明该 构造函数 :

template <typename T>
class Student
{
public:
	Student(T x, T y);
}

2、类模板 外部 实现 普通函数


将 类内部的 普通函数 实现 加法运算符重载 的函数 , 提取到 类模板 外部进行定义 ;

该函数的 返回值 和 参数 都涉及到 类模板 类型 ;

template <typename T>
class Student
{
public:

	// 重载 + 运算符
	Student operator+(Student& s)
	{
		Student student(this->a + s.a, this->b + s.b);
		return student;
	}
}

在类外部 实现 该 加号运算符重载 需要注意以下几点 :

  • 首先 , 需要 声明 模板类型 , template <typename T> ;
  • 然后 , 通过 域操作符 访问 构造函数 , Student<T>:: 后面跟上要访问的成员 ;
  • 最后 , 返回值和参数类型 , 如果是 类模板类型 Student , 需要在后面使用尖括号 指明具体的类型 , 这里具体的类型就是泛型 T ;

函数内部 Student 类型 , 可以加 <T> 也可不加 <T> , 不加 <T> 也可以使用 , 加了也不会报错 ;

// 重载 + 运算符
// 使用  Student<T>:: 域操作符访问函数
template <typename T>
Student<T> Student<T>::operator+(Student<T>& s)
{
	// 函数内部的类的 <T> 模板类型 , 可加 <T> 可不加 <T> 
	// 不加 <T> 也可以使用 , 加了也不会报错
	Student student(this->a + s.a, this->b + s.b);
	return student;
}

类模板内部 , 需要声明该 重载函数 ;

template <typename T>
class Student
{
public:
	// 重载 + 运算符
	Student operator+(Student& s);
}

3、类模板 外部 实现 友元函数


友元函数 不是 类中的函数 , 是 类外部的函数 , 友元函数 中又用到了 泛型 T , 说明这是一个 模板函数 ;

友元函数 是 全局函数 , 不属于 类模板 , 不要使用 域操作符 访问友元函数 ;

友元函数 中的 泛型类型 , 要当做 函数模板 对待 ;

模板函数就涉及到 二次编译 问题 , 下面先分析一下 模板函数 二次编译 导致的 类模板的友元函数 问题 ;

友元函数 不要乱用 , 只有在 重载 左移 右移 操作符时 , 才使用 友元函数 ;



( 1 ) 错误示例及分析 - 类模板 的 外部友元函数 二次编译 问题

在 类模板 内部声明 友元函数 ,

template <typename T>
class Student
{
	// 左移运算符重载
	friend ostream& operator<<(ostream& out, Student& s);
}

在 类外部 实现 友元函数 ,

// Student 类的友元函数
// 左移运算符重载 函数
template <typename T>
ostream& operator<<(ostream& out, Student<T>& s)
{
	out << "a:" << s.a << " b: " << s.b << endl;
	return out;
}

运行时会报如下错误 :

已启动生成…
1>------ 已启动生成: 项目: HelloWorld, 配置: Debug Win32 ------
1>Test.obj : error LNK2019: 无法解析的外部符号 "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Student<int> &)" (??6@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AAV01@AAV?$Student@H@@@Z),函数 _main 中引用了该符号
1>D:\002_Project\006_Visual_Studio\HelloWorld\HelloWorld\Debug\HelloWorld.exe : fatal error LNK1120: 1 个无法解析的外部命令
1>已完成生成项目“HelloWorld.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0==========

造成上述错误的原因 就是 函数模板 的实现机制 中的 二次编译 有关 ,

  • 第一次编译 函数模板 时 , 只进行 简单的 语法分析 , 词法分析 , 生成一个函数头 ;
  • 第二次编译 函数模板 时 , 又生成一个 函数头 ;

这两次编译生成的 函数头 不一致 , 导致 无法找到 相应的 函数实现 ;


( 2 ) 正确写法

友元函数 不要乱用 , 只有在 重载 左移 右移 操作符时 , 才使用 友元函数 ;


这是 函数模板 二次编译 问题 ,

一般情况下 , 函数模板 只有在 调用时 , 才需要将 泛型类型 指明 , 在 函数名称后面 , 使用 <> 注明泛型类型 ,

但是在 类模板 声明 友元函数 时 , 就需要指定 泛型类型 ;

这样才能将 类模板中的 泛型 T , 与 友元函数在 外部实现时 声明的 template <typename T> 关联起来 ;


在 类模板 内部声明 友元函数时 , 在函数名 operator<< 后面 加上 <T> ;

template <typename T>
class Student
{
	// 左移运算符重载
	friend ostream& operator<< <T> (ostream& out, Student& s);
}

在 类外部 实现 友元函数 保持不变 ;

// Student 类的友元函数
// 左移运算符重载 函数
template <typename T>
ostream& operator<<(ostream& out, Student<T>& s)
{
	out << "a:" << s.a << " b: " << s.b << endl;
	return out;
}




二、代码示例 - 函数声明与函数实现分离




1、代码示例


#include "iostream"
using namespace std; 

template <typename T>
class Student
{
	// 左移运算符重载
	friend ostream& operator<< <T> (ostream& out, Student& s);

public:
	// 构造函数
	Student(T x, T y);

	// 重载 + 运算符
	Student operator+(Student& s);

public:
	T a, b;
};

// 类模板构造函数
// 使用  Student<T>:: 域操作符访问函数
template <typename T>
Student<T>::Student(T x, T y)
{
	this->a = x;
	this->b = y;
}

// 重载 + 运算符
// 使用  Student<T>:: 域操作符访问函数
template <typename T>
Student<T> Student<T>::operator+(Student<T>& s)
{
	// 函数内部的类的 <T> 模板类型 , 可加 Student<T> 可不加 Student
	// 不加 <T> 也可以使用 , 加了也不会报错
	Student student(this->a + s.a, this->b + s.b);
	return student;
}

// Student 类的友元函数
// 左移运算符重载 函数
template <typename T>
ostream& operator<<(ostream& out, Student<T>& s)
{
	out << "a:" << s.a << " b: " << s.b << endl;
	return out;
}

int main() {
	// 模板类不能直接定义变量
	// 需要将 模板类 具体化之后才能定义变量
	Student<int> s(666, 888);
	cout << s << endl;

	Student<int> s2(222, 111);
	cout << s2 << endl;

	// 验证 加法运算符 + 重载
	Student<int> s3 = s + s2;

	// 验证 左移运算符 << 重载
	cout << s3 << endl;
	
	// 控制台暂停 , 按任意键继续向后执行
	system("pause");

	return 0;
}

2、执行结果


执行结果 :

a:666 b: 888

a:222 b: 111

a:888 b: 999

Press any key to continue . . .

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/173976.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【运维】永久关闭selinux不当,导致无法启动

现象: 卡centos loading进度条 按esc键发现,启动报错: Failed to load SElinux policy ,freezing 可能的原因: selinuxdisabled 写错成disable 或者 错误的把selinuxtype改了&#xff0c;要改文中红框的部分。 解决方案: 1. 重启 2. 出现选择画面的时候 按e 3. 方向下键…

树莓派的的串口通信协议

首先&#xff0c;回顾一下串口的核心知识点&#xff0c;也是面试重点&#xff1a; 串口通信通常使用在多机通讯中串口通信是全双工的决定串口通信的成功与否的是 数据格式 和 波特率数据格式&#xff1a;1. 数据位 2.停止位 3. 奇偶校验位 树莓派恢复串口 回忆前几节树莓派刷机…

时序预测 | Pytorch实现TCN-Transformer的时间序列预测

时序预测 | Pytorch实现TCN-Transformer的时间序列预测 目录 时序预测 | Pytorch实现TCN-Transformer的时间序列预测效果一览基本介绍程序设计 效果一览 基本介绍 基于TCN-Transformer模型的时间序列预测&#xff0c;可以用于做光伏发电功率预测&#xff0c;风速预测&#xff0…

【libGDX】使用Mesh绘制三角形

1 Mesh 和 ShaderProgram 简介 1.1 创建 Mesh 1&#xff09;Mesh 的构造方法 public Mesh(boolean isStatic, int maxVertices, int maxIndices, VertexAttribute... attributes) public Mesh(boolean isStatic, int maxVertices, int maxIndices, VertexAttributes attribut…

使用VSCode+PlatformIO搭建ESP32开发环境

Arduino IDE本来就是为创客们开发的&#xff0c;虽然没代码提示功能&#xff0c;文件的关系也不清晰&#xff0c;函数不能跳转&#xff0c;头文件也打不开&#xff0c;但人家的初衷就是为了简单而生的&#xff1b;但还是有一些同学喜欢高级点的IDE&#xff0c;也没问题&#xf…

局域网无法上网主机通过TinyProxy代理主机访问公网Internet

1.代理主机搭建: 系统:ubuntu 网卡:2个 运行于 VMWare上 第一个网卡用于NAT 第二个网卡用于私有网络 两个IP如下: 192.168.31.243为NAT可访问Internet 192.168.144.141属于私有网络,用于访问局域网 安装tinyproxy sudo apt install tinyproxy 查看服务状态

场景驱动的 AI 体验设计:如何让智能 IDE 赋能遗留系统重写

作为 AutoDev 的核心开发&#xff0c;我们不仅在不断丰富 AutoDev 的功能以满足不同公司的定制需求&#xff0c;还在与各种团队进行持续交流。在处理遗留系统时&#xff0c;我们发现程序员们日常工作中需要面对大量使用过时技术、基础设施混乱的系统。 在这个背景下&#xff0c…

智能座舱架构与芯片- (6) 显示篇 上

一、概述 在智能座舱的发展历程中&#xff0c;显示屏的个数越来越多&#xff0c;分辨率和显示屏的尺寸也越来越大。这已经是不可逆转的趋势。传统的座舱显示屏需要一颗主芯片支持一块屏幕&#xff0c;这在功能上和成本上都不是很好的做法。最新的智能座舱解决方案中&#xff0…

阿里 OSS鉴权访问文件

如果OSS文件设置保护&#xff0c;需要鉴权才能访问&#xff0c;添加请求头鉴权&#xff0c;SDK方法如上&#xff1b; 将鉴权信息和地址、时间返回给前端&#xff0c;前端直接从oss上读取 String filePath "/admin/2023/6/183569314928918546.png"; RequestMessage…

【python笔记】客户运营 - cohort分析

一、数据 本文涉及数据下载链接。 二、数据预处理 2.1 读取数据 import pandas as pddf pd.read_csv(your_path/Year 2010-2011.csv, encodingISO-8859-1) df.head()2.2 检查数据 检查空值情况 df.isna().sum() # 结果 Invoice 0 StockCode 0 De…

基于SpringBoot+MyBatis-Plus的校园图书管理系统

基于SpringBootMyBatis-Plus的校园图书管理系统 校园图书管理系统开发技术功能模块代码结构数据库设计运行截图源码获取 校园图书管理系统 欢迎访问此博客&#xff0c;是否为自己的毕业设计而担忧呢&#xff1f;是否感觉自己的时间不够做毕业设计呢&#xff1f;那你不妨看一下…

python趣味编程-5分钟实现一个简单弹跳球游戏(含源码、步骤讲解)

简单的Python弹跳球程序是使用Python编程语言开发的。 Python 中的弹跳球游戏是 使用 Tkinter 和图形用户界面 (GUI) 设计的,它是一个桌面应用程序。 Python 中的弹跳球游戏代码使用Canvas 在 Python 中绘制对象和随机模块。

ES7-ES13有何新特性?

目录 ES7 ES8 ES9 ES10 ES11 ES12 ES13 hello&#xff0c;大家好呀&#xff01;之前发布的两篇关于ES6新特性的文章学习完了吗&#xff1f;今天来给大家介绍ES6之后&#xff0c;截止到2022年的ES13每个时期新增的一些新特性&#xff01;快来一起学习吧&#xff01; ES7 …

【Computer Vision Foundation】全球计算机视觉基金会论文网

计算机视觉基金会&#xff08;Computer Vision Foundation&#xff0c;简称CVF&#xff09;是一个致力于推动计算机视觉领域研究和发展的组织。以下是关于计算机视觉基金会的一些基本信息&#xff1a; 成立目的&#xff1a; CVF成立的目的是促进计算机视觉领域的学术研究、技术…

【C+进阶之路】第六篇:C++11

文章目录 一、【C】C11&#xff08;1&#xff09;二、【C】C11&#xff08;2&#xff09; 一、【C】C11&#xff08;1&#xff09; 【C】C11&#xff08;1&#xff09; 二、【C】C11&#xff08;2&#xff09; 【C】C11&#xff08;2&#xff09; &#x1f339;&#x1f33…

时间复杂度和运算

时间复杂度 在算法和数据结构中&#xff0c;有许多时间复杂度比 O(1) 更差的情况。以下是一些常见的时间复杂度&#xff0c;按照从最优到最差的顺序排列&#xff1a; O(1)&#xff1a; 常数时间复杂度&#xff0c;操作的运行时间与输入规模无关&#xff0c;是最理想的情况。 O…

ky10 server arm 在线编译安装openssl3.1.4

在线编译脚本 #!/bin/shOPENSSLVER3.1.4OPENSSL_Vopenssl versionecho "当前OpenSSL 版本 ${OPENSSL_V}" #------------------------------------------------ #wget https://www.openssl.org/source/openssl-3.1.4.tar.gzecho "安装OpenSSL${OPENSSLVER}...&q…

nodejs微信小程序+python+PHP-维斯公司财务管理系统的设计与实现-计算机毕业设计推荐

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

IP地址定位是如何实现的?

IP定位的实现是通过查找IP地址对应的地理位置信息来实现的。具体来说&#xff0c;IP定位是通过查询IP地址对应的地理位置数据库来获取地理位置信息。这个数据库可以是公共的或者私有的&#xff0c;其中包含了IP地址和地理位置信息之间的映射关系。 在实际操作中&#xff0c;IP定…

如何实现车机体验”遥遥领先”?头部玩家已经给出答案

车机与手机的深度融合&#xff0c;通过跨终端互联互通实现全场景、沉浸式的用户体验&#xff0c;正在成为各大高端智能汽车品牌的新战场。 此前&#xff0c;已经有华为、苹果几大手机巨头已经纷纷开启“造车”业务&#xff0c;同时吉利等车企也反向进入手机领域&#xff0c;各…