随笔分类 - 《C++ Primer 4th edition》概览与浅析
摘要:[0. 必须定义为类成员的操作符]赋值 =;下标 [];调用 ();成员访问箭头 ->;[1. 输入和输出操作符]支持 I/O 操作的类所提供的 I/O 操作接口,一般应该与标准库 iostream 为内置类型定义的接口相同,因此,许多类都需要重载输入和输出操作符。为了与 IO 标准库一致,操作符应接受 ostream& 作为第一个形参,对类类型 const 对象的引用作为第二个形参,并返回对 ostream 形参的引用。重载输出操作符一般的简单定义如下:// general skeleton of the overloaded output operatorostream&am
阅读全文
摘要:复制控制是定义任意 C++ 类必不可少的部分。当定义一个新类型的时候,需要显式或隐式地指定复制、赋值和撤销该类型的对象时会发生什么。以上这些是通过定义复制构造函数、赋值操作符和析构函数来达到的。如果没有显式定义复制构造函数或赋值操作符,编译器(通常)会为我们定义。复制构造函数,具有单个形参,该形参(常用 const 修饰)是对该类类型的引用。1) 当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数。2) 当将该类型的对象传递给函数或函数返回该类型的对象时,将隐式使用复制构造函数。析构函数:当对象超出作用域或动态分配的对象被删除时,将自动应用析构函数。1) 析构函数可用
阅读全文
摘要:设计具有指针成员的类时,类设计者必须首先需要决定的是该指针应提供什么行为。将一个指针复制到另一个指针时,两个指针指向同一对象。当两个指针指向同一对象时,可能使用任一指针改变基础对象。类似地,很可能一个指针删除了一对象时,另一指针的用户还认为基础对象仍然存在。指针成员默认具有与指针对象同样的行为。然而,通过不同的复制控制策略,可以为指针成员实现不同的行为。大多数 C++ 类采用以下三种方法之一管理指针成员:1. 指针成员采取常规指针型行为。这样的类具有指针的所有缺陷但无需特殊的复制控制。2. 类可以实现所谓的“智能指针”行为。指针所指向的对象是共享的,但类能够防止悬垂指针。3. 类采取值型行为。
阅读全文
摘要:0. 复制构造函数只有单个形参,而且该形参是对本类类型对象的引用(常用 const 修饰),这样的构造函数称为复制构造函数。与默认构造函数一样,复制构造函数可由编译器隐式调用。复制构造函数可用于: 根据另一个同类型的对象显式或隐式初始化一个对象。 复制一个对象,将它作为实参传给一个函数。 从函数返回时复制一个对象。 初始化顺序容器中的元素。 根据元素初始化式列表初始化数组元素。对象的定义形式回忆一下,C++ 支持两种初始化形式:直接初始化和复制初始化。复制初始化使用 = 符号,而直接初始化将初始化式放在圆括号中。当用于类类型对象时,初始化的复制形式和直接形式有所不同:直接初始化直接调...
阅读全文
摘要:0. 使用背景对于特定类类型的全体对象而言,访问一个全局对象有时是必要的。也许,在程序的任意点需要统计已创建的特定类类型对象的数量;或者,全局对象可能是指向类的错误处理例程的一个指针;或者,它是指向类类型对象的内在自由存储区的一个指针。然而,全局对象会破坏封装:对象需要支持特定类抽象的实现。如果对象是全局的,一般的用户代码就可以修改这个值。1. static 类成员类可以定义类 静态成员,而不是定义一个可普遍访问的全局对象。通常,非 static 数据成员存在于类类型的每个对象中。不像普通的数据成员,static 数据成员独立于该类的任意对象而存在;每个 static 数据成员是与类关联的对象
阅读全文
摘要:友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类。友元的声明以关键字 friend 开始。它只能出现在类定义的内部。友元声明可以出现在类中的任何地方:友元不是授予友元关系的那个类的成员,所以它们不受声明出现部分的访问控制影响。通常,将友元声明成组地放在类定义的开始或结尾是个好主意。友元关系:一个例子想像一下,除了 Screen 类之外,还有一个窗口管理器,管理给定显示器上的一组 Screen。窗口管理类在逻辑上可能需要访问由其管理的 Screen 对象的内部数据。假定 Window_Mgr 是该窗口管理类的名字,Screen 应该允许 Window_Mgr 像下面这样访问其成员:c
阅读全文
摘要:C++ 标准化过程做出的更重要的贡献之一是:创建和扩展了标准库。容器和算法库是标准库的基础。标准库定义了超过一百个算法。幸运的是,这些算法具有相同的结构,使它们更易于学习和使用。算法与类型无关:它们通常在一个元素序列上操作,这些元素可以存储在标准库容器类型、内置数组甚至是生成的序列(例如读写流所生成的序列)上。算法基于迭代器操作,从而实现类型无关性。大多数算法使用一对指定元素范围的迭代器作为其头两个实参。其他的迭代器实参包括指定输出目标的输出迭代器,或者用于指定第二个输入序列的另一个或一对迭代器。迭代器可通过其所支持的操作来分类。标准库定义了五种迭代器类别:输入、输出、前向、双向和随机访问迭代
阅读全文
摘要:list 容器上的迭代器是双向的,而不是随机访问类型。由于 list 容器不支持随机访问,因此,在此容器上不能使用需要随机访问迭代器的算法。这些算法包括 sort 及其相关的算法。还有一些其他的泛型算法,如 merge、remove、reverse 和 unique,虽然可以用在 list 上,但却付出了性能上的代价。如果这些算法利用 list 容器实现的特点,则可以更高效地执行。如果可以结合利用 list 容器的内部结构,则可能编写出更快的算法。与其他顺序容器所支持的操作相比,标准库为 list 容器定义了更精细的操作集合,使它不必只依赖于泛型操作。表 11.4 列出了 list 容器特有的
阅读全文
摘要:【3. 反向迭代器】反向迭代器是一种反向遍历容器的迭代器。其将自增(和自减)的含义反过来了:对于反向迭代器,++ 运算将访问前一个元素,而 -- 运算则访问下一个元素。所有容器还定义了 rbegin 和 rend 成员,分别返回指向容器尾元素和首元素前一位置的反向迭代器。与普通迭代器一样,反向迭代器也有常量(const)和非常量(nonconst)类型。图 11.1 使用一个假设名为 vec 的 vector 类型对象阐明了这四种迭代器之间的关系。假设有一个 vector 容器对象,存储 0-9 这 10 个以升序排列的数字:vector vec;for (vector::size_type
阅读全文
摘要:【0. 概述总览】之前已强调标准库所定义的迭代器不依赖于特定的容器。事实上,C++ 语言还提供了另外 3 种迭代器:插入迭代器: 与容器绑定在一起,实现在容器中插入元素的功能。 iostream 迭代器: 可与输入或输出流绑定在一起,用于迭代遍历所关联的 IO 流。反向迭代器: 实现向后遍历,而不是向前遍历。所有容器类型都定义了自己的 reverse_iterator 类型, 由 rbegin 和 rend 成员函数返回。上述迭代器类型都在 iterator 头文件中定义。【1. 插入迭代器】通过插入迭代器赋值时,迭代器将会插入一个新的元素。C++ 语言提供了 ...
阅读全文
摘要:[0. 需求]最近在粗略学习《C++ Primer 4th》的容器内容,关联容器的章节末尾有个很不错的实例。通过实现一个简单的文本查询程序,希望能够对C++的容器学习有更深的理解。由于是浅略探讨研究,高手可无视,各位读者发现有什么不妥的地方,请指教。程序将读取用户指定的任意文本文件,然后允许用户从该文件中查找单词。查询的结果是该单词出现的次数,并列出每次出现所在的行。如果某单词在同一行中多次出现,程序将只显示该行一次。行号按升序显示,即第 1行应该在第 2 行之前输出,依此类推。本人用到的文本文件“ inputfile.txt ”,内容如下(显示的行号并非文件原内容):1 Our progra
阅读全文
摘要:map 和 set 容器中,一个键只能对应一个实例。而 multiset 和 multimap 类型则允许一个键对应多个实例。例如,在电话簿中,每个人可能有单独的电话号码列表;在作者的文章集中,每位作者可能有单独的文章标题列表。multimap/ultiset 类型的定义也在 map 和 set 头文件。multimap/multiset 所支持的操作与 map/set 只有一个不同:multimap 不支持下标运算。因为在这类容器中,某个键可能对应多个值。因此,multiset/multimap 对应 set/map 中相同的操作都以不同的方式做出了一定的修改。[1. 元素的添加和删除]in
阅读全文
摘要:set类型map 容器是键-值对的集合,好比以人名为键的地址和电话号码。相反地,set 容器只是单纯的键的集合。map 适用于字典、电话本、商品价目表等类似的模型。set 适用于黑名单、白名单等。// set 容器支持大部分的 map 操作,包括下面几种:1. 顺序容器和关联容器公共的操作通用的容器操作。2. 构造函数。(map<k, v> m; map<k, v> m(m2); map<k, v> m(b, e); )3. nsert 操作。(m.insert(e); m.insert(beg, end); m.insert(iter, e); )4. c
阅读全文
摘要:map 是键-值对的集合。map 类型通常可理解为关联数组(associative array):可使用键作为下标来获取一个值,正如内置数组类型一样。而关联的本质在于元素的值与某个特定的键相关联,而并非通过元素在数组中的位置来获取。[1. map 对象的定义]要使用 map 对象,则必须包含 map 头文件。在定义 map 对象时,必须分别指明键和值的类型:// count number of times each word occurs in the inputmap<string, int> word_count; // empty map from string to int
阅读全文
摘要:[0. 概述]关联容器和顺序容器的本质差别在于:关联容器通过键(key)存储和读取元素,而顺序容器则通过元素在容器中的位置顺序存储和访问元素。虽然关联容器的大部分行为与顺序容器相同,但其独特之处在于支持键的使用。关联容器(Associative containers)支持通过键来高效地查找和读取元素。两个基本的关联容器类型是 map 和 set。map 的元素以键-值(key-value)对的形式组织:key 用作元素在 map 中的索引,而 value 则表示所存储和读取的数据。set 仅包含一个键,并有效地支持关于某个键是否存在的查询。一般来说,如果希望有效地存储不同值的集合,那么使用 s
阅读全文
摘要:除了顺序容器,标准库还提供了三种顺序容器适配器:queue、priority_queue 和 stack。适配器(adaptor)是标准库中通用的概念,包括容器适配器、迭代器适配器和函数适配器。本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制。容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。例如,stack(栈)适配器可使任何一种顺序容器以栈的方式工作。表 9.22 列出了所有容器适配器通用的操作和类型。// 表 9.22. 适配器通用的操作和类型size_type 一种类型,足以存储此适配器类型最大对象的长度value_type 元素类型...
阅读全文
摘要:/*** 本节是作为《进一步探讨 string 类型》的详细补充,* * 可直接跳过,也可以先粗略浏览。* * 可以在编写程序时遇到有关 string 的这些操作时,才回来阅读其细节。**/[1. 构造 string 对象的其他方法]string 类支持表 9.2 所列出的几乎所有构造函数,只有一个例外:string 不支持带有单个容器长度作为参数的构造函数。创建 string 对象时:不提供任何参数,则得到空的 string 对象;也可将新对象初始化为另一个 string 对象的副本;或用一对迭代器初始化:或者使用一个计数器和一个字符初始化:string s1; // s1 is the e
阅读全文
摘要:[0. 回望复习]之前介绍了 string 类型,表 9.12 扼要重述了在该节中介绍的 string 操作。// 表 9.12 string s; 定义一个新的空 string 对象,命名为 sstring s(cp); 定义一个新的 string 对象,用 cp 所指向的(以空字符 null 结束的)C 风格字符串初始化该对象string s(s2); 定义一个新的 string 对象,并将它初始化为 s2 的副本is >> s; 从输入流 is 中读取一个以空白字符分隔的字符串,写入 sos << s; 将 s 写到输出流 os 中getl...
阅读全文
摘要:[1. 关系操作符]所有的容器类型都支持用关系操作符来实现两个容器的比较。相互比较的容器必须具有相同的容器类型,而且其元素类型也必须相同。容器的比较实质上是基于容器内元素的比较。如果容器的元素类型不支持某种操作符,则该容器就不能做这种比较运算。下面的操作类似于 string 类型的关系运算:如果两个容器具有相同的长度而且所有元素都相等,那么这两个容器就相等;否则,它们就不相等。如果两个容器的长度不相同,但较短的容器中所有元素都等于较长容器中对应的元素,则称较短的容器小于另一个容器。如果两个容器都不是对文的初始子序列,则它们的比较结果取决于所比较的第一个不相等的元素。理解上述操作的最简单方法是研
阅读全文
摘要:C++ 的每种顺序容器都提供了一组有用的类型定义和以下操作: 1. 在容器中添加元素。 2. 在容器中删除元素。 3. 设置容器大小。 4. (如果有的话)获取容器内的第一个和最后一个元素。//所有容器都提供下表所列出的类型size_type 无符号整型,足以存储此容器类型的最大可能容器长度iterator 此容器类型的迭代器类型const_iterator 元素的只读迭代器类型reverse_iterator 按逆序寻址元素的迭代器const_reverse_iterator 元素的只读(不能写)逆序迭代器difference_t...
阅读全文