12.C++模板进阶 | 代码膨胀

目录

0.引入

函数模板

类模板

1. 非类型模板参数

运用 array

2. 函数模板的特化

2.1 概念

2.2 类模板的特化

全特化

偏特化

3. 模板不可以分离编译

回顾类和对象

3.1 什么是分离编译

3.2 模板的分离编译

4. 模板总结

代码膨胀

代码膨胀的影响:

代码膨胀的应对方法:


0.引入

函数模板

模板类型感悟:

template<class T>
void Swap(T& rx, T& ry) {T tmp = rx;

测试:

template<class T>
void Swap(T& rx, T& ry) {T tmp = rx;rx = ry;ry = tmp;
}int main(void)
{int a = 0, b = 1;double c = 1.1, d = 2.2;char e = 'e', f = 'f';Swap(a, b);Swap(c, d);Swap(e, f);return 0;
}

运行:

类模板

函数模板传的是类型,类模板传的是模板

类模板要实例化

template<class Container>
void Print(const Container& v)
{//传&可以进行修改,传的是模板类// typename Container::const_iterator it = v.begin();auto it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;
}

为什么要考虑这样写 typename Container::const_iterator it = v.begin();

编译不确定Container::const_iterator是类型还是对象,typename就是明确告诉编译器这里是类型,等模板实例化再去找,因为静态的也可以这样写,存在混淆

测试:

int main()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto e : v){cout << e << " ";}cout << endl;Print(v);list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);for (auto e : lt){cout << e << " ";}cout << endl;Print(lt);return 0;
}


1. 非类型模板参数

模板参数分类类型形参与非类型形参。

类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。

非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

特点:还传了 size_t N

  1. 整型
  2. 常量

刚好也一般都是用来定义数组

template<class T, size_t N>
class Stack
{
public:void func(){// 常量,不能修改N = 0;}
private:T _a[N];int _top;
};

在实际场景中,运行结果如下:

注意:

1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。

2. 非类型的模板参数必须在编译期就能确认结果


运用 array

和数组有什么区别呢?

貌似没有...(C++11 更新出来的)

对越界检查非常的严格~

 //C++11// // 鸡肋//int a[10];array<int, 10> a;a[0] = 0;for (auto e : a){cout << e << " ";}//array对越界的检验非常严格,越界读写都能检查// 普通数组,不能检查越界读,少部分越界写可以检查//a[10];//vector<int> v(10, 0);

array 挺少用的,了解一下就好啦

因为话说这样写

v[10] vector检查不香吗?还可以初始化


2. 函数模板的特化

2.1 概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{return left < right;
}int main()
{cout << Less(1, 2) << endl; // 可以比较,结果正确Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl; // 可以比较,结果正确Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 可以比较,结果错误return 0;
}

模板无法对指针进行比较,怎么办呢?--特化

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
//函数模板
template<class T>
bool Less(T left, T right)
{return left < right;
}//针对某些类型进行的特化 ---Date*
template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;//比较解引用的值
}

2.2 类模板的特化

全特化

全特化即是将模板参数列表中所有的参数都是确定化。

template<class T1,class T2>
class Data
{
public:Data(){cout << "Data<T1,T2>" << endl;}
private:T1 _d1;T2 _d2;
};//全特化
template<>
class Data<double, int>//参数匹配上了,就走这条
{
public:Data(){cout << "Data<double,int>" << endl;}
};int main()
{Data<int, int> d1;Data<double, double> d2;//特化Data<double, int> d3;//直接走特化return 0;
}

偏特化

偏特化:针对模板参数进一步进行条件限制设计的特化版本。

1.部分特化

将模板参数表中的一部分参数特化

template<class T1>
class Data<T1,char>
{
public:Data(){cout << "Data<T,char>" << endl;}
private:T1 _d1;
};

不管第一个参数是什么,只要第二个参数是我需要的特化,就去走那个特化。

2.参数进一步限制

偏特化并不仅仅是指特化部分参数,也有针对模板参数更进一步的条件限制所设计出来的一个特化版本。

//两个参数偏特化为指针类型
template<class T1, class T2>
class Data<T1*,T2*>
{
public:Data(){cout << "Data<T1*,T2*>" << endl;}
private:T1 _d1;T2 _d2;
};//两个参数偏特化为指针类型
template<class T1, class T2>
class Data<T1&, T2&>
{
public:Data(){cout << "Data<T1&,T2&>" << endl;}
private:T1 _d1;T2 _d2;
};


3. 模板不可以分离编译

回顾类和对象

可见往期文章2.C++类和对象(上)

  1. 大型项目:类声明放在.h文件中成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::

类的命名规则:

一般在成员变量前加_,来区分赋值,例如 _year= year,这样就区分开了

  1. 类的访问限定符及封装

访问限定符
用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用,以此来对程序进行保护,防止其被破坏

  1. public修饰的成员在类外可以直接被访问
  2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)

例如

#include <iostream>
using namespace std;class Example {
private:int privateVar;void privateMethod() {cout << "This is a private method." << endl;}public:int publicVar;void publicMethod() {cout << "This is a public method." << endl;}// Public method to set the private variablevoid setPrivateVar(int value) {privateVar = value;}// Public method to get the private variableint getPrivateVar() {return privateVar;}
};int main() {Example obj;// Accessing public member variableobj.publicVar = 10;cout << "Public variable: " << obj.publicVar << endl;// Accessing public member methodobj.publicMethod();// Accessing private member variable through public methodobj.setPrivateVar(20);cout << "Private variable (accessed through public method): " << obj.getPrivateVar() << endl;// Error: Accessing private member variable directly// obj.privateVar = 30; // Uncommenting this line will cause a compilation error// Error: Accessing private member method directly// obj.privateMethod(); // Uncommenting this line will cause a compilation errorreturn 0;
}

解释

  • 正确的调用
    • obj.publicVar = 10;:设置公共变量的值。
    • obj.publicMethod();:调用公共方法。
    • obj.setPrivateVar(20);obj.getPrivateVar()通过公共方法间接访问私有变量。
  • 错误的调用
    • obj.privateVar = 30;:直接访问私有变量会导致编译错误,因为privateVar是私有的。
    • obj.privateMethod();:直接调用私有方法会导致编译错误,因为privateMethod是私有的。

3.1 什么是分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链 接起来形成单一的可执行文件的过程称为分离编译模式。

3.2 模板的分离编译

假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:

template<class T>
T Add(const T& left, const T& right);// a.cpp
template<class T>
T Add(const T& left, const T& right)
{return left + right;
}// main.cpp
#include"a.h"
int main()
{Add(1, 2);Add(1.0, 2.0);return 0;
}

分析:

修改:

将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。

#ifndef ADD_HPP
#define ADD_HPP// 模板函数声明和定义
template<class T>
T Add(const T& left, const T& right) {return left + right;
}#endif // ADD_HPP

4. 模板总结

【优点】

  1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
  2. 增强了代码的灵活性

【缺陷】

  1. 模板会导致代码膨胀问题,也会导致编译时间变长
  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

代码膨胀

代码膨胀(Code Bloat)是指软件中的代码量过大,且可能包含许多冗余、重复或不必要的代码,从而导致程序变得庞大且效率低下。代码膨胀通常在以下几种情况下发生:

1. 模板和泛型编程

  • 在使用C++模板时,如果同一个模板被实例化为多个类型,那么编译器会为每个实例生成独立的代码。这会导致二进制文件的体积增加。例如,std::vector<int>std::vector<double> 会生成两套不同的代码。

2. 宏和预处理器指令

  • 使用大量的宏定义和预处理器指令会生成许多重复的代码片段,增加了代码的体积和复杂性。

3. 过度使用设计模式

  • 一些设计模式(如工厂模式、单例模式等)如果过度使用或滥用,可能会引入许多额外的代码,增加了程序的复杂性。

4. 缺乏代码优化和重构

  • 在软件开发过程中,缺乏代码优化和重构可能会导致大量冗余代码的累积。例如,重复的函数、无效的变量声明和不必要的对象创建。

5. 库和框架的使用

  • 使用大型库或框架时,往往会引入许多不必要的功能和代码,导致程序体积膨胀。

代码膨胀的影响:

1.性能

  • 代码膨胀会导致程序的加载时间增加,运行速度变慢,内存占用增大,影响整体性能。

2.维护性

  • 过多的冗余代码使得代码的可读性和可维护性降低,增加了维护的难度和成本。

3.编译时间

  • 大量的代码会显著增加编译时间,影响开发效率。

代码膨胀的应对方法:

1.模板优化

  • 使用模板元编程技巧来减少不必要的模板实例化
  • 使用类型擦除(type erasure)技术来减少模板代码膨胀

2.宏定义的合理使用

  • 尽量减少宏定义的使用,使用内联函数或常量表达式替代。

3.适度使用设计模式

  • 在适当的场景下使用设计模式,不滥用设计模式。

4.定期进行代码重构

  • 定期进行代码重构,删除冗余代码,优化代码结构,提高代码质量。

5.选择合适的库和框架

  • 使用轻量级的库和框架,根据需求选择合适的第三方工具,避免引入过多的无关功能。

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

STM32 BootLoader 刷新项目 (五) 获取软件版本号-命令0x51

STM32 BootLoader 刷新项目 (五) 获取软件版本号-命令0x51 下面我们来讲解第一个指令&#xff0c;获取软件版本号命令-0x51. 在BootLoader中获取软件版本号的操作有多个重要的作用&#xff0c;具体如下&#xff1a; 版本管理&#xff1a; 识别当前版本&#xff1a;通过获取软…

AI+折叠屏,荣耀的创新周期论

文&#xff5c;刘俊宏 编&#xff5c;王一粟 2024年&#xff0c;AI和折叠屏的演进路线&#xff0c;已经成为了手机行业的共识。 首先&#xff0c;手机市场的新增量已经被折叠屏所接管。据Counterpoint Research数据显示&#xff0c;中国2024年第一季度折叠屏手机销量同比增长…

BUUCTF逆向wp [HDCTF2019]Maze

第一步 查壳&#xff0c;本题是32位&#xff0c;有壳&#xff0c;进行脱壳。 第二步 这里的 jnz 指令会实现一个跳转&#xff0c;并且下面的0EC85D78Bh被标红了&#xff0c;应该是一个不存在的地址&#xff0c;这些东西就会导致IDA无法正常反汇编出原始代码&#xff0c;也称…

【系统架构设计】数据库系统(一)

数据库系统&#xff08;一&#xff09; 数据库模式与范式数据库的结构与模式数据模型关系代数数据的规范化反规范化 数据库设计事务管理备份与恢复分布式数据库系统数据仓库数据挖掘NoSQL大数据 数据库模式与范式 数据库的结构与模式 数据库技术中采用分级的方法将数据库的结…

萝卜快跑无人出租车是有人远程代驾? 客服:没有人操控

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 近期“萝卜快跑”无人驾驶网约车相关话题引发网友热议。 有网传图片显示&#xff0c;萝卜快跑机器人智控中心&#xff0c;有真人坐在带有方向盘的屏幕前&#xff1b; 有网友认为所谓的无人网约车&am…

【设计模式】【创建型模式】【02工厂模式】

系列文章 可跳转到下面链接查看下表所有内容https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501文章浏览阅读2次。系列文章大全https://blog.csdn.net/handsomethefirst/article/details/138226266?spm1001.2014.3001.5501 目录 系…

C++链接FTP服务器并下载数据(在qt中编写)

.pro文件 #------------------------------------------------- # # Project created by QtCreator 2024-07-16T13:19:03 # #-------------------------------------------------QT core gui networkgreaterThan(QT_MAJOR_VERSION, 4): QT widgetsTARGET untitled TE…

通过SchedulingConfigurer 接口完成动态定时任务

通过SchedulingConfigurer 接口完成动态定时任务 一.背景 在Spring中&#xff0c;除了使用Scheduled注解外&#xff0c;还可以通过实现SchedulingConfigurer接口来创建定时任务。它们之间的主要区别在于灵活性和动态性。Scheduled注解适用于固定周期的任务&#xff0c;一旦任…

【C++数据结构】二叉搜索树(超详细图解操作过程,超详细讲解代码实现)

目录 01.二叉搜索树的概念 02.二叉搜索树的操作过程 03.二叉搜索树的代码实现 &#xff08;1&#xff09;基本框架 &#xff08;2&#xff09;树的创建与销毁 &#xff08;3&#xff09;元素的查找 &#xff08;4&#xff09;元素的插入 &#xff08;5&#xff09;元素的…

Day71 代码随想录打卡|回溯算法篇---全排列

题目&#xff08;leecode T46&#xff09;&#xff1a; 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 方法&#xff1a;全排列是数学中的基础问题&#xff0c;也是回溯算法能解决的经典问题。全排列因为每个元素都会…

卷积神经网络学习问题总结

问题一&#xff1a; 深度学习中的损失函数和应用场景 回归任务&#xff1a; 均方误差函数&#xff08;MSE&#xff09;适用于回归任务&#xff0c;如预测房价、预测股票价格等。 import torch.nn as nn loss_fn nn.MSELoss() 分类任务&#xff1a; 交叉熵损失函数&…

AI算法19-偏最小二乘法回归算法Partial Least Squares Regression | PLS

偏最小二乘法回归算法简介 算法概述 偏最小二乘法模型可分为偏最小二乘回归模型和偏最小二乘路径模型。其中偏最小二乘回归模型是一种新型的多元统计方法&#xff0c;它集中了主成分分析、典型相关分析和线性回归的特点&#xff0c;特别在解决回归中的共线性问题具有无可比拟…

PX4 运行 make px4_sitl_default gazebo 报错

报错原因&#xff1a;最开始我把依赖一直都是在base环境下安装的&#xff0c;没有conda deactivate&#xff0c;而pip install的东西应该装在系统环境&#xff0c;不能装在base环境下&#xff0c;sudo apt 是装在系统环境的 1.检查ros 用鱼香ros安装 wget http://fishros.…

日活2.5亿的Twitter 使用了哪些数据库?

Twitter 使用什么数据库存储用户每天发送的数亿条推文&#xff1f;是 SQL、NoSQL 还是其它持久化存储系统&#xff1f; Twitter 使用什么数据库&#xff1f; 任何一个稍微有点规模的系统其存储层绝不会只使用一种数据库&#xff0c;服务于数以亿计用户的Twitter更是如此。Twit…

《YOLOv10改进实战专栏》专栏介绍 专栏目录

《YOLOv10改进实战专栏》介绍及目录 YOLOv10官方仓库地址 专栏地址&#xff1a;点击跳转 专栏导航如下&#xff1a; &#x1f380;基础入门篇&#x1f380; 万字长文&#xff0c;小白新手怎么开始做YOLO实验&#xff0c;从零开始教&#xff01;整体思路在这里&#xff0c;科研指…

Vue学习---vue cli 项目创建

使用的编辑工具webStorm 创建例子: hello vue create hello 选择 vue3 进行创建 运行 npm run serve 测试访问&#xff1a;http://localhost:8080 改动内容重新编译&#xff1a; npm run build dist 目录就是编译后的可运行内容

浅谈C嘎嘎类与对象

本篇文章与大家浅谈一下C嘎嘎的类与对象知识点 类的定义 关键字&#xff1a;class 语法格式&#xff1a; class 类名 { }&#xff1b;//这里的分号不能少 此外&#xff0c;class有三个属性分别是private、public、protected&#xff0c;这三个属性是干啥的&#xff0c;相…

MSPM0G3507——时钟主频拉到80MHZ

先点开使用时钟树 在配置时钟界面这样配置

Ghost Browser指纹浏览器年+IPXProxy代理IP组合:SheIn卖家必看

SheIn是一家时尚电商公司&#xff0c;其用户数量近年来增长迅速&#xff0c;在全球的知名度越来越高。SheIn跨境电商卖家想要提升店铺曝光和排名&#xff0c;从而增加销量和信誉的话&#xff0c;就需要满足独立IP、模拟设备参数、独立环境等条件。同时满足这些条件的话就需要用…

生成式人工智能(AI)的未来

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…