IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++代码优化 Chapter1 -> 正文阅读

[C++知识库]C++代码优化 Chapter1

作者:recommend-item-box type_blog clearfix

本文将介绍一些常见的 C++ 代码优化技巧,本文为该系列的第一章,未来不定期更新。

使用前置递增(递减)运算符

可能很多从 C 语言开始学习的朋友会对此感到困惑,觉得这两者在适用的情况下可以任意选择,如 for 循环语句写作 for (int i = 0; i < n; i++) 。这样做当然没错,但会造成一定程度上的资源浪费,因为后置递增运算符需要先将原本的变量值保存下来,再对其进行递增操作,而在此 for 循环中,我们并不需要使用原本的 i 值。并且 ++i 的写法相对来说更符合我们的意愿。

当然对于基本数据类型来讲,在经过编译器的优化之后,两者的效率可能并没有什么差别,但对于 STL 中的模板容器,或是自定义数据结构的迭代器来讲,前置运算符的效率显然要更高,因为单个对象所占的内存空间更大,使得拷贝暂存的开销也越大。

因此,除非必要情况,应该尽可能使用前置递增(递减)运算符。

使用引用传递

引用类型变量并不是一个对象,它只是一个已存在对象的别名,因此在作为变量传递时不会经过拷贝构造的过程,能够显著地提升效率,以下为值传递的情况。

struct myStruct
{
	int x, y, z;
	myStruct(int x, int y, int z) : x(x), y(y), z(z) {}
	myStruct (const myStruct& ms) : x(ms.x), y(ms.y), z(ms.z) {
		std::cout << "copy constructor is called!" << std::endl;
	}
};

void printMs(myStruct ms) { 
	std::cout << ms.x << " " << ms.y << " " << ms.z << std::endl;
}

int main()
{
	myStruct ms(1, 2, 3);
	printMs(ms); // 输出"copy constructor is called!"
	std::cin.get();
}

事实上,我们只是想通过调用该函数输出结构体对象的成员变量值,因此并不需要对原对象进行拷贝,这将会带来不必要的资源浪费,因此函数的参数应该使用引用传递,此外,由于函数只需要进行输出操作,而不涉及对象修改,因此参数可以加上 const 限定符,将原函数修改为 void printMs(const myStruct& ms) .

我们知道 C++ 11 引入了一种新的 for 循环语句格式:for (type element : array),即遍历 array 中的所有元素,因此每次循环都将执行一次 element = array[i]; ++i;,因此如果 element 不是引用类型,则每次循环都将进行一次拷贝,这同样会造成资源浪费。正确的写法应该是 for (type& element : array),如无需更改对象值,则最好加上 const 限定符。

此外C++ 11 还加入了自动类型推导 auto 关键字,虽然该关键字不宜滥用,但在基于范围的 for 循环中十分适用,尤其在面对一些较复杂的类型名时,可以使得代码更加简洁:for (auto& element : array).

使用 emplace() 替代 push()

emplace() 函数同样是 C++ 11 引入的新特性,用以替代原来的 push()

以往我们希望向 STL 模板容器中添加元素时,通常会先创建一个元素对象,再调用 push() 将其添加进容器中(此处的 push() 指的是 push_back(), push_front(), insert() 等一系列函数)。这样相当于需要先构造原对象,再拷贝该对象。但如果我们并没有事先创建这个对象,而是希望添加具有特定参数值的对象,那这样做无疑会带来不必要的性能开销,那有没有一种方法能够让我们直接在容器内构造对象呢?emplace() 就具备这样的功能,以下示例对比了 emplace_back() 和 push_back() 的使用方法,并通过重写构造函数和拷贝构造函数来追踪构造和拷贝的调用情况。

struct myStruct
{
	int x, y, z;
	myStruct(int x, int y, int z) : x(x), y(y), z(z) {
		std::cout << "constructor is called!" << std::endl;
	}
	myStruct (const myStruct& ms) : x(ms.x), y(ms.y), z(ms.z) {
		std::cout << "copy constructor is called!" << std::endl;
	}
};

int main()
{
	std::vector<myStruct> msVec;
#if 1
	myStruct ms(1, 2, 3);
	msVec.push_back(ms);
    // 输出
    // constructor is called!
	// copy constructor is called!
#endif

#if 0
	msVec.emplace_back(1, 2, 3);
#endif
	std::cin.get();
}

可见,push_back() 的原理如上所述,调用了一次构造函数和一次拷贝构造函数。注意,此处的 #if <expression> <block> #endif 是预处理操作,当 #if 后的 <expression> 为真,则执行 <block> 代码,否则不执行,因此该操作在这里用作注释。

当我们执行 emplace_back() 时,结果如下:

int main()
{
	std::vector<myStruct> msVec;
#if 0
	myStruct ms(1, 2, 3);
	msVec.push_back(ms);
#endif

#if 1
	msVec.emplace_back(1, 2, 3);
    // 输出 constructor is called!
#endif
	std::cin.get();
}

当我们希望原地构造时,emplace_back() 参数与元素对象类型的构造函数相同。同样的我们也可以传入一个已经存在的对象,这样效果和 push_back() 相同。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-10 22:13:22  更:2022-03-10 22:14:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:04:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码