Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >STL:调用empty()而不是检查size()是否为0

STL:调用empty()而不是检查size()是否为0

作者头像
用户6557940
发布于 2023-03-05 09:48:25
发布于 2023-03-05 09:48:25
1.4K00
代码可运行
举报
文章被收录于专栏:Jungle笔记Jungle笔记
运行总次数:0
代码可运行

如果要判断一个容器是否为空,如何判断呢?

一种方式是,调用size()函数,判断其是否等于0

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
stl_container a;
if (a.size() == 0) 
{
  std::cout << " a is empty!" << std::endl;
 }

另一种方式是,调用empty()函数。各类STL容器都提供了empty()函数,如果为空,则empty()返回true;否则返回false。

两种方式都可以,而且本质上都是判断容器的size是否为0。在日常开发中,出于个人习惯,并不会特别在意非要调用哪一种。

而《Effective STL》给出的建议是,调用empty()

为什么呢?

因为不同容器的empty()实现,一定是耗费常数时间,而size()则不一定

为此,我查看了我工作环境上的几个容器的empty()实现,分别如下(说明,g++ 7.5.0, c++14)。

std::vector

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool empty()
{
  return begin() == end();
}

vector是检查首尾两个迭代器是否相等。vector底层是一块连续的内存,其迭代器本质上是指向这块内存首尾位置的两个指针。所以empty()函数是在检查这两个指针是否指向同一位置,若是,则说明容器为空,返回true。这当然是常数时间。

std::deque

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool empty()
{
  return M.finish == M.start;
}

和vector一样,也是检查首尾指针是否指向同一处,也是常数时间。deque底层是分段连续的内存组成的一块“表面”连续的buffer,这是和vector的区别,所以其迭代器的实现多有区别,不过迭代器的本质仍旧是指针。

std::array

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool empty()
{
  return size() == 0;
}

array的实现,则是直接调用size()函数,判断其内部维护的私有变量M_Nm是否为0。

std::string

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool empty()
{
  return size() == 0;
}

string的size()返回的是内部维护的私有变量M_string_length。

std::list

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool empty()
{
  return this->M_node->next == &this->M_node;
}

list的empty()是判断当前节点的next指针是否指向自己,同样是常数时间的操作。

std::set

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bool empty()
{
  return this->node_count == 0;
}

set的底层是红黑树,维护了一个node_count的变量,通过判断node_count是否为0可以在常数时间内得到结果。

std::unordered_set

unordered_set的emtpy()实现也是判断size()==0。而size()返回的是内部维护的私有变量M_element_count。

我没有再查看其他容器的实现,上述列出的容器几乎代表所有stl容器类型。尽管上述各个容器的empty()的实现和其容器底层密切相关,但总体都是耗费常数时间

那么size()的实现就不是常数时间了吗?

上面可以看到,array,set,unordered_set都是内部维护了一个私有成员变量size,其各个改变容器成员大小的成员函数都会更新这个size。这些容器的size()同样是常数时间的操作。

也可以想见,vector的size()实现,是将首尾两个迭代器相减,因为vector底层是一块内存连续的buffer。两个指针相减,这也是常数时间。同理,deque也是。

既然如此,为什么不推荐使用size() == 0呢?

答案是,list的一些实现,size耗费线性时间,即list独有的splice操作。不过这取决于各家的编译器的实现。

splice会将指定链表对象上指定范围内的元素切下来接到目标对象的指定位置后,会同时改变两个链表对象的size。

我查看了我的编译器版本上的splice的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void splice(iterator __position, list& __x, iterator __first, iterator __last)
{
  if (__first != __last)
  {
      if (this != std::__addressof(__x))
        _M_check_equal_allocators(__x);

      size_t __n = _S_distance(__first, __last);
      this->_M_inc_size(__n);
      __x._M_dec_size(__n);

      this->_M_transfer(__position._M_const_cast(),
            __first._M_const_cast(),
            __last._M_const_cast());
  }
}

可以看到,list内部也维护了类似于size的成员,上面代码中调用_S_distance函数以获得被切链表部分的长度__n的目的,即是更新当前链表和被切链表的size信息。所以这个版本的list的size()也是常数时间。

我顺带查看了list的erase()、insert()等函数的实现,发现它们内部都在维护size的状态。这说明,这版编译器为了使得size()为常数时间性能,牺牲了部分成员函数——如splice()——的性能。比如splice()函数内部的_S_distance()函数,由链表的本质可以知道,它一定会遍历,从而耗费线性时间。

那么如果splice的实现中,没有去更新两个链表的size信息呢?那么当用户调用size()的时候,这个size()函数返回什么呢?它一定是去遍历整个链表,耗费线性时间后,得到size信息,再返回给用户。

而《Effective C++》这一节所强调的,正是stl中各个容器设计时关于empty()函数与别的成员函数之间的性能取舍问题。当然,如上所述,性能优劣并不是绝对的,取决于各家编译器的实现。Anyway,可以保证的是,empty()函数,一定是常数时间的性能。

所以,如果在开发中遇到需要判断容器是否为空的时候,推荐大家使用empty(),而不是判断size() == 0。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-12-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Jungle笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【C++】基础:STL标准库常用模块使用
C++标准模板库(Standard Template Library,STL)是C++中的一个重要组成部分,提供了丰富的容器、算法和函数模板,可以帮助开发人员快速实现通用的数据结构和算法。STL的设计目标是提供高效、可靠、易于使用的工具,以提高开发效率和代码可维护性。
DevFrank
2024/07/24
2120
STL 总结与常见面试题
为了建立数据结构和算法的一套标准,并且降低他们之间的耦合关系,以提升各自的独立性、弹性、交互操作性(相互合作性,interoperability),诞生了STL。
C语言与CPP编程
2020/10/16
9350
《逆袭进大厂》第四弹之C++重头戏STL30问30答
这是《逆袭进大厂》系列的第四期,本期是 C++ 重头戏,也就是标准模板库 STL 的内容,本期是 24098 个字。
拓跋阿秀
2021/03/21
1.6K0
深入理解STL库_STL文件格式的工作原理
上期说过C++这块面试问的东西也蛮多,简历上只要出现C++这几个字,那么STL库就是必问。
全栈程序员站长
2022/11/03
6670
深入理解STL库_STL文件格式的工作原理
STL容器的线程安全性了解多少?
STL的意思是与迭代器合作的C++标准库的一部分,包括标准容器(包括string),iostream库的一部分,函数对象和算法,它不包括标准容器适配器(stack,queue和priority_queue)以及bitset和valarray容器,因为它们缺乏迭代器的支持,也不包括数组。数组以指针的形式支持迭代器,但数组是C++语言的一部分,并非库。
用户9831583
2022/12/04
1.6K0
掌握 C++ 标准库(STL):理解STL的核心概念
STL定义了强大的、基于模板的、可复用的组件,实现了许多通用的数据结构及处理这些数据结构的算法。其中包含三个关键组件——容器(container,流行的模板数据结构)、迭代器(iterator)和算法(algorithm)。
Lion 莱恩呀
2025/01/18
5720
掌握 C++ 标准库(STL):理解STL的核心概念
C++ STL 标准模板库(容器总结)算法
C++ 标准模板库STL,是一个使用模板技术实现的通用程序库,该库由容器container,算法algorithm,迭代器iterator,容器和算法之间通过迭代器进行无缝连接,其中所包含的数据结构都是目前最优解,该库既能保证软件代码的高可复用性,又能保证代码具有相当高的执行效率,STL库是ANSI/ISO的C++标准的具体实现,任何标准库的实现都是以源码形式释出的.
王 瑞
2022/12/28
2.4K0
读完两遍《STL源码剖析》后,我发现了一些辛秘
对于每一位学习 C++ 的小伙伴来说,STL 不可谓不重要,特别是那些为我们造好的底层轮子比如容器、算法等更是一件利器,比如在一些 OJ 平台,用 STL 下的算法刷题简直不要太爽,谁用谁知道。
拓跋阿秀
2021/04/26
3.5K0
读完两遍《STL源码剖析》后,我发现了一些辛秘
【C++】<STL部分>:STL标准模板库概要
STL(standard template libaray-标准模板库),是C++标准库的重要组成部分,包含了很多常用的数据结构和算法。
Skrrapper
2025/04/08
1390
【C++】<STL部分>:STL标准模板库概要
从c++到golang,golang中的对应C++的STL是哪些
C++中的std::string是一个可变的数据结构,用于处理文本数据。Go中的字符串是不可变的,但Go提供了丰富的字符串处理函数。
GeekLiHua
2024/08/30
2130
【C++篇】STL中list的奥秘与实现解析
上篇文章我们学习了vector,本文开始学习list。 list即为数据结构中的链表,我们在学习初阶数据结构的时候已经学习过他的结构了并用C语言实现了他,。我们知道,链表分为是否带头、是否双向和是否循环,因此有8种链表。而功能最强大的就是带头双向循环链表,因此STL选用了他作为list。STL也有单链表,它是forword_list,但用的很少。 【初探数据结构】线性表————链表(一)(单链表的实现) 【初探数据结构】线性表——链表(二)带头双向循环链表(详解与实现)
我想吃余
2025/05/28
760
【C++篇】STL中list的奥秘与实现解析
C++ stl_stl函数
长久以来,软件界一直希望建立一种可重复利用的东西,以及一种得以制造出”可重复运用的东西”的方法,从函数(functions),类别(classes),函数库(function libraries),类别库(class libraries)、各种组件,从模块化设计,到面向对象(object oriented ),为的就是复用性的提升。
全栈程序员站长
2022/09/27
2.5K0
【c++】标准模板库STL入门简介与常见用法
STL(Standard Template Library)标准模板库,主要由容器、迭代器、算法、函数对象、内存分配器和适配器六大部分组成。STL已是标准C++的一部分,使用STL开发系统可以提高开发效率。
马三小伙儿
2018/09/12
7700
C++13-STL模板
在线练习: http://noi.openjudge.cn/ https://www.luogu.com.cn/
IT从业者张某某
2023/10/16
3310
C++13-STL模板
一万五千字C++STL【容器】详解 (全网最详细)
一般大多数的题目都可以使用vector容器,除非有特定需求使用其他容器更加合理方便;
C语言与CPP编程
2023/09/06
3.1K0
一万五千字C++STL【容器】详解 (全网最详细)
【C++100问】深度总结STL基本容器的使用
文章首发于本人CSDN账号:https://blog.csdn.net/tefuirnever
我是管小亮
2020/04/20
1.2K0
C++ STL源码剖析之双向环形链表list
双向环状链表从节点值为3开始插入,红色框表示最后一个节点(end()指向的节点)。黄色线条表示指向前驱节点,黑色线条表示指向后继节点。
公众号guangcity
2019/10/09
1.7K0
C++STL——哈希
在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到
有礼貌的灰绅士
2023/06/14
5660
C++STL——哈希
C++系列笔记(十)
【导读】《21天学通C++》这本书通过大量精小短悍的程序详细而全面的阐述了C++的基本概念和技术,包括管理输入/输出、循环和数组、面向对象编程、模板、使用标准模板库以及创建C++应用程序等。这些内容被组织成结构合理、联系紧密的章节,每章都可在1小时内阅读完毕,都提供了示例程序清单,并辅以示例输出和代码分析,以阐述该章介绍的主题。本文是系列笔记的第十篇,欢迎各位阅读指正!
墨明棋妙27
2022/08/24
5440
10min快速回顾C++语法(八)STL专题
⭐写在前面的话:本系列文章旨在短时间内回顾C/C++语法中的重点与易错点,巩固算法竞赛与写题过程中常用的语法知识,精准地解决学过但有遗忘的情况,为算法刷题打下坚实的基础。
timerring
2022/09/26
3110
相关推荐
【C++】基础:STL标准库常用模块使用
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验