C++标准程序库笔记之一
本篇博客笔记顺序大体按照《C++标准程序库(第1版)》各章节顺序编排。
--------------------------------------------------------------------------------------------
2. C++及其标准程序库简介
2.2-1
注意:如果要把一个template中的某个标识符号指定为一种型别,就算意图显而易见,关键字typename也不可或缺,因此C++的一般规则是,除了以typename修饰之外,template内的任何标识符号都被视为一个值(value)而非一个型别。如:
// 关键字typename被用来作为型别之前的标识符号
template <class T>
class MyClass
{
typename T::Subtype * ptr;
....
};
2.2-2
class member function 可以是个template(类的成员函数模板),但这样的member template 既不能是virtual,也不能有缺省参数。(原因?好像《Effective C++ 》有讲过这个问题*-*)
2.2-3
template <class T>
class MyClass
{
private:
T value;
public:
template <class X>
void assign(const MyClass<X>& x)
{
// this型别可以直接取用(在类里面操作私有或保护成员);x作为一个对象,要取用私有或保护成员,必须使用getValue()之类的接口
// this.value = x.getValue;
value = x.getValue();
};
T getValue() const
{
return value;
}
};
2.2-4
Template constructor 是member template 的一种特殊形式。Template constructor 通常用于“在复制对象时实现隐式型别转换”。注意,template constructor并不遮蔽implicit copy constructor。如果型别完全吻合,implicit copy constructor (隐式拷贝构造函数)就会被产生出来并被调用。如下:
template <class T>
class MyClass
{
public:
template <class U>
MyClass (const MyClass<U>& x);
...
};
void f()
{
MyClass <double> xd;
...
MyClass<double> xd2(xd); // calls built-in copy constructor
MyClass<int> xi(xd); // calls template constructor
...
};
2.2-5
异常throw 语句开始了stack unwinding(堆栈辗转开解)过程,也就是说,它将使得退离任何函数区段时的行为就像以return语句返回一样,然后程序却不会跳转到任何地点。对于所有被声明于某区段——而该区段却因程序异常而退离——的局部对象而言,其destructor(析构函数)会被调用。Stack unwinding的动作会持续知道退出main() 或直到有某个catch子句捕捉并处理了该异常为止。
2.2-6
命名空间namespace与class雷同,要引用该namespace内的符号,必须加上namespace标识符。不同于class的是,namespace是开放的,你可以在不同模块(modules)中定义和扩展namespace(也即,和class 不同,namespace 具有扩展开放性,可以出现在任何源码文件中。因此你可以利用一个namespace来定义一些组件,而它们可散步于多个实质模块上)。
2.2-7
根据C++标准规格,只有两种mian() 是可移植的:
int main()
{
.....
}
// 和
int main(int argc, char* argv[])
{
....
}
这里argv(命令行参数数组)也可定义为char**。请注意,由于不允许“不言而喻”的返回型别int,所以返回型别必须明白写为int。你可以使用return语句来结束main(),但不必一定如此。这一点和C不同,换句话说,C++在main()的末尾定义了一个隐式的: return 0; 这意味如果你不采用return 语句离开main(),实际上就表示成功退出(传回任何一个非零值都代表某种失败)。
--------------------------------------------------------------------------------------------
3. 一般概念
3.4-1
将C++标准程序库中所有标识符都定义于namespace std里头,这种做法是标准化过程中引入的。这个做法不具向下兼容性,因为原先的C/C++头文件都将C++标准程序库的标识符定义于全局范围(global scope)。此外标准化过程中有些classes 的接口也有了更动。为此,特别引入了一套新的头文件命名风格。如:
#include <string> // C++ class string
#include <cstring> // char* functions from C, was : <string.h>
#include <cstdlib> // char* functions from C, was : <stdlib.h>
3.4-2
标准异常类别可分为三组:
(1)语言本身支持的异常;
(2)C++标准程序库发出的异常;
(3)程序作用域(scope of a program)之外发出的异常。
所有标准异常的接口只含一个成员函数:what(),用以获取“型别本身以外的附加信息”,它返回一个以null结束的字符串。除此之外,再没有任何异常提供任何其它成员函数,能够描述异常的种类。
3.4-3
C++标准程序库在许多地方采用特殊对象来处理内存配置和寻址,这样的对象称为配置器(allocator)。配置器体现出一种特定的内存模型(memory model),成为一个抽象表征,表现出“内存需求”至“内存低阶调用”的转换。如果运用多个不同的配置器对象,你便可以在同一个程序中采用不同的内存模型。
--------------------------------------------------------------------------------------------
4. 通用工具
4.2-1
参见博客 为什么需要auto_ptr_ref
4.3-1
数值极限
一般来说,数值型别的极值是一个与平台相关的特性。C++标准程序库通过template numeric_limits提供这些极值,取代传统C语言所采用的预处理常数。C++ Standard规定了各种型别必须保证的最小精度。
4.4-1
函数swap用来交换两对象的值。前提,只有当swap()所依赖的copy构造操作和assignment操作行为存在时,这个调用才可能有效。swap()的最大优势在于,透过template specialization(模板特化)或function overloading(函数重载),我们可以为更复杂的型别提供特殊的实作版本。
4.6-1
头文件<cstddef>和<cstdlib>和其C对应版本兼容。注意,C语言中的NULL通常定义为(void*)0。在C++中这并不正确,NULL的型别必须是个整数型别,否则你无法将NULL赋值给一个指针。这是因为C++并没有定义从void*到任何其他型别的自动转型操作。
4.6-2
函数exit()和abort()可用来在任意地点终止程序运行,无需返回main():
(1)exit()会销毁所有static对象,将所有缓冲区(buffer)清空(flushes),关闭所有I/O通道(channels),然后终止程序(之前会先调用经由atexit()登录的函数)。如果atexit()登录的函数抛出异常,就会调用terminate()。
(2)abort()会立刻终止函数,不做任何清理(clean up)工作。
这两个函数都不会销毁局部对象(local objects),因为堆栈辗转开展动作(stack unwinding)不会被执行起来。为确保所有局部对象的析构函数获得调用,你应该运用异常(exceptions)或正常返回机制,然后再由main()离开。
--------------------------------------------------------------------------------------------
5. Standard Template Library(STL),标准模板库
5.3-1 迭代器
(1)牢记一点,每一种容器都提供了自己的迭代器(《STL源码剖析》有详细解释)。这些迭代器了解该种容器的内部结构,所以能够知道如何正确行进,事实上,每一种容器都将其迭代器以嵌套(nested)方式定义于内部,因此各种迭代器的接口相同,型别却不同。透过迭代器的协助,我们只需撰写一次算法(注意,算法并非容器类别的成员函数,而是一种搭配迭代器使用的全局函数),就可以将它应用于任意容器之上,这是因为所有容器的迭代器都提供一致的接口(容器--迭代器--算法)。
(2)Multimaps不允许我们使用subscript(下标)操作符,因为multimaps允许单一索引对应到多个不同元素,而下标操作符却只能处理单一实值。你必须先产生一个“键值/实值”对组,然后再插入multimap。
5.4-1
算法 如果某个算法用来处理多个区间,那么当你调用它时,务必确保第二(以及其它)区间所拥有的元素个数,至少和第一区间内的元素个数相同。特别是,执行涂写动作时,务必确保目标区间够大。
5.6-1
更易型算法
(1)算法不能自己移除容器元素,这是STL为了获取灵活性而付出的代价。透过“以迭代器为接口”,STL将数据结构和算法分离开来。然而,迭代器只不过是“容器中某一位置”的抽象概念而已。一般来说,迭代器对自己所属的容器一无所知。任何“以迭代器访问容器元素”的算法,都不得(也无法)透过迭代器调用容器类别所提供的任何成员函数。
(2)切记:更易型算法(指那些会移除remove、重排resort、修改modify元素的算法)都不得用于关联式容器身上,因为如果更易型算法用于关联式容器身上,会改变某位置上的值,进而破坏其已序(sorted)特性,那也就违反了关联式容器的基本原则:容器内的元素总是根据某个排序准则自动排序。因此,为了保证这个原则,关联式容器的所有迭代器均被声明为指向常量(const iterator)。每一种关联式容器都提供用以移除元素的成员函数。
(3)有时,针对一个容器,标准库同时提供了STL算法和容器成员函数执行相同操作,如果想要有更高效率,那么使用容器成员函数会是更优选择,因为STL算法针对的是所有容器进行抽象实现。代价是,一旦更换另一种容器,就不得不改动程序代码。
5.9-1
仿函数(functors,function objects)
(1)仿函数是“smart functions”(智能型函数) “行为类似指针”的对象,我们称为“smart pointers”。“行为类似函数” 的对象,我们也可以称为“smart functions”,因为它们的能力可以超越 operator ()。仿函数可以拥有成员函数和成员变量,这意味仿函数拥有状态(state)。事实上,<在同一时间里,由某个仿函数所代表的单一函数>,可能有不同的状态。这在一般函数中是不可能的。另一个好处是,你可以在执行期初始化它们——当然必须在它们被使用(被调用)之前。
class AddValue
{
private:
int theValue; // the value to add
public:
AddValue(int v) : theValue(v) { }
void operator () (int& elem) const
{
elem += theValue;
}
}; int main()
{
list<int> coll;
for(int i = ; i <= ; ++i)
{
coll.push_back(i);
} print_elements(coll, "initialized : "); // 打印容器元素 for_each(coll.begin(), coll.end(),
AddValue()); // 执行期才指定数值,通过仿函数的成员变量theValue保存这个状态 print_elements(coll, "after adding 10 : "); // 打印容器元素 for_each(coll.begin(), coll.end(),
AddValue(*coll.begin())); print_elements(coll, "after adding first element : "); // 打印容器元素
} 输出:
initialized :
after adding :
after adding first element :
(2)每个仿函数都有自己的型别
<一般函数,唯有在它们的标记式(signatures)不同时,才算型别不同。而仿函数即使标记式相同,也可以有不同的型别>。事实上,由仿函数定义的每一个函数行为都有其自己的型别。这对于“利用template实现泛型编程”乃是一个卓越贡献,因为如此一来,我们便可以将函数行为当做template参数来运用。这使得不同型别的容器可以使用同类型的仿函数作为排序准则。这可以确保你不会在排序准则不同的群集之间赋值、合并和比较。你甚至可以设计仿函数继承体系,以此完成某些特别事情,例如在一个总体原则下确立某些特殊情况。
AddValue addx(x); // function object that adds value x
AddValue addy(y); // add y for_each(coll.begin(), coll.end(), addx);
...
for_each(coll.begin(), coll.end(), addy);
...
for_each(coll.begin(), coll.end(), addx);
(3)仿函数通常比一般函数速度快
就template概念而言,由于更多细节在编译期就已确定,所以通常可能进行更好的最佳化。所以,传入一个仿函数(而非一般函数),可能获得更好的性能。
仿函数相关的更多信息参见《STL源码剖析》
5.10-1
Value语意VS. Reference语意
所有容器都会建立元素副本,并返回该副本。这意味容器内的元素与你放进去的对象“相等(equal)”但非“同一(identical)”。如果你修改容器中的元素,实际改变的是副本而不是原先对象。这意味STL容器所提供的是“value语意”。它们所容纳的是你所安插的对象值,而不是对象本身。然而实用上你也许需要用到“reference语意”,让容器容纳元素的reference。
STL只支持value语意,不支持reference语意,好处:
(1)元素的拷贝很简单;
(2)使用reference时容易导致错误。你必须确保reference所指向的对象仍然健在,并需小心对付偶尔出现的循环引用状态。
缺点:
(1)“拷贝元素”可能导致不好的效能;有时甚至无法拷贝;
(2)无法在数个不同的容器中管理同一份对象。
实用上你同时需要两种作法。你不但需要一份独立(于原先对象)的拷贝(此乃value语意),也需要一份代表原书记、以能相应改变原值的拷贝(此乃reference语意)。不幸的是,C++标准程序库不支持reference语意。不过我们可以利用value语意来实现reference语意。 一个显而易见的方法是以指针作为元素(C程序员或许很能认可“以指针实现reference语意”的手法。因为在C语言中函数的参数只能passed by value(传值),因此需要通过指针才能实现所谓的call by reference)。为了避免资源泄漏,可以使用智能指针。然而我们不能使用auto_ptr,因为它不符合作为容器元素所需的基本要求。当auto_ptr执行了拷贝(copy)或赋值(assign)动作后,目标对象与原对象并不相等:原来的那个auto_ptr发生了变化,其值并不是被拷贝了,而是被转移了。 你可以使用带有“引用计数”的智能指针实现STL容器的reference语意,但即使这样也很麻烦,举个例子,如果你拥有直接存取元素的能力,你就可以更改元素值,而这在关联式容器中却会打破元素顺序关系。
《C++标准程序库》6.8节提供了一个实现STL容器reference语意的例子。
5.11-1
STL的设计原则是效率优先,安全次之。错误检查相当花时间,所以几乎没有。C++标准程序库指出,对于STL的任何运用,如果违反规则,将会导致未定义行为。 但C++标准程序库还是提供了相应保证,如表6.35.
注意,所有这些保证都有一个前提:析构函数不得抛出异常。
C++标准程序库笔记之一的更多相关文章
- 《C++标准程序库》笔记之二
<C++标准程序库>笔记之二 本篇博客笔记顺序大体按照<C++标准程序库(第1版)>各章节顺序编排. ------------------------------------- ...
- 《C++标准程序库》笔记之四
本篇博客笔记顺序大体按照<C++标准程序库(第1版)>各章节顺序编排. ---------------------------------------------------------- ...
- 《C++标准程序库》笔记之三
本篇博客笔记顺序大体按照<C++标准程序库(第1版)>各章节顺序编排. ---------------------------------------------------------- ...
- 《C++标准程序库》学习笔记(一)C++相关特性
抱着本厚厚的<C++标准库>读了几天,想想也该写点关于用法的总结,一来怕今后容易忘记,二来将书上的事例重新敲一遍,巩固对程序库相关知识的了解.今天开第一篇,以后不固定更新.当然,笔者所读为 ...
- <<C++标准程序库>>中的STL简单学习笔记
0. 内容为个人学习笔记, 仅供参考, 如有错漏, 欢迎指正! 1. STL中的所有组件都是由模板构成的, 所以其元素可以是任意型别的. 组件有: - 容器: 管理某类对象的集合. 不同的容器有各自的 ...
- STL学习笔记(第二章 C++及其标准程序库简介)
本章重点是介绍与C++标准程序库相关的几个最重要的语言新特性 template(模板) 程序库中几乎所有东西都被设计成template形式.所谓templates,是针对“一个或多个尚未明确的型别”所 ...
- C++标准程序库读书笔记-第四章通用工具
1.Pairs(对组) (1)class pair可以将两个值视为一个单元.任何函数需返回两个值,也需要pair. (2)便捷地创建pair对象可以使用make_pair函数 std::make_pa ...
- C++标准程序库读书笔记-第二章新的语言特性
1.基本类型的显式初始化 如果采用不含参数.明确的constructor(构造函数)调用语法,基本型别会被初始化为零: int i1; //undefined value int i2 = int() ...
- STL笔记(4)关于erase,remove
STL笔记(4)关于erase,remove 你要erase的元素很容易识别.它们是从区间的“新逻辑终点”开始持续到区间真的终点的原来区间的元素.要除去那些元素,你要做的所有事情就是用那两个迭代器调用 ...
随机推荐
- 实践:C++平台迁移以及如何用C#做C++包装层
在前面,我们看过OpenTK与MOgre,这二个项目都是C#项目,但是他的实现都是C++.他们简单来说就是一个包装层.常见的包装方式有二种,一种就是我们熟知的显式P/Invoke(DllImport) ...
- Linux强制杀进程命令行工具
需求, 有时候我们会有手动启动程序, 但是又在后台, 没有像服务那样有start, 和stop的程序, 这时候需要用强制杀进程方式 涉及工具, awk, sed, xargs, kill 需求一: 已 ...
- 【Python】python3-list列表引用
print(names) #列出列表的内容 print(names[3]) #访问列表中第4个值 print(names[1:3]) #访问列表中从第2个到第3个的值 print(names[-1]) ...
- 最近迷上了GUI
package windows; import java.awt.BorderLayout; import javax.swing.ButtonGroup; import javax.swing.JB ...
- Mybatis系列(五):mybatis逆向工程
一.背景 在实际开发中我们会自己去写mapper映射文件,接口,数据库表对应的实体类,如果需求任务比较少,咱们还可以慢慢的一个一个去写,但是这是不现实的,因为在工作中我们的任务是很多的,这时mybat ...
- (原)SDL调试心得
今天在项目中用到SDL2.0的库做视频显示用,在其中出现不少问题,这里一一记录下来,并作为以后的参考. 同一个窗口句柄在多次使用SDL_CreateWindowFrom和SDL_DestroyWind ...
- CI框架 -- 开发环境、生产环境
开发者常常希望当系统运行在开发环境或生产环境中时能有不同的行为, 例如,在开发环境如果程序能输出详细的错误信息将非常有用,但是在 生产环境这将造成一些安全问题. ENVIRONMENT 常量 Code ...
- 使用ConcurrentLinkedQueue惨痛的教训【转】
转自:http://blog.csdn.net/jackpk/article/details/49634577 服务端原本有个定时任务对一个集合ArrayList 中的消息做处理. 因为考虑到处理消息 ...
- Tomcat5内存简单优化
tomcat版本:apache-tomcat-5.5.28 解压版tomcat 修改catalina.bat文件,在开头添加如下内容: JAVA_OPTS='-Xms1024m -Xmx2048m - ...
- [原创] MSP430G2系列图形化编程相关资料
1.TI官方工具GRACE以及CCS介绍以及下载地址:http://www.ti.com.cn/tool/cn/grace 2.教程资料: ①手把手教你使用GRACE: http://www.do ...