本篇博客笔记顺序大体按照《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++标准程序库笔记之一的更多相关文章

  1. 《C++标准程序库》笔记之二

    <C++标准程序库>笔记之二 本篇博客笔记顺序大体按照<C++标准程序库(第1版)>各章节顺序编排. ------------------------------------- ...

  2. 《C++标准程序库》笔记之四

    本篇博客笔记顺序大体按照<C++标准程序库(第1版)>各章节顺序编排. ---------------------------------------------------------- ...

  3. 《C++标准程序库》笔记之三

    本篇博客笔记顺序大体按照<C++标准程序库(第1版)>各章节顺序编排. ---------------------------------------------------------- ...

  4. 《C++标准程序库》学习笔记(一)C++相关特性

    抱着本厚厚的<C++标准库>读了几天,想想也该写点关于用法的总结,一来怕今后容易忘记,二来将书上的事例重新敲一遍,巩固对程序库相关知识的了解.今天开第一篇,以后不固定更新.当然,笔者所读为 ...

  5. <<C++标准程序库>>中的STL简单学习笔记

    0. 内容为个人学习笔记, 仅供参考, 如有错漏, 欢迎指正! 1. STL中的所有组件都是由模板构成的, 所以其元素可以是任意型别的. 组件有: - 容器: 管理某类对象的集合. 不同的容器有各自的 ...

  6. STL学习笔记(第二章 C++及其标准程序库简介)

    本章重点是介绍与C++标准程序库相关的几个最重要的语言新特性 template(模板) 程序库中几乎所有东西都被设计成template形式.所谓templates,是针对“一个或多个尚未明确的型别”所 ...

  7. C++标准程序库读书笔记-第四章通用工具

    1.Pairs(对组) (1)class pair可以将两个值视为一个单元.任何函数需返回两个值,也需要pair. (2)便捷地创建pair对象可以使用make_pair函数 std::make_pa ...

  8. C++标准程序库读书笔记-第二章新的语言特性

    1.基本类型的显式初始化 如果采用不含参数.明确的constructor(构造函数)调用语法,基本型别会被初始化为零: int i1; //undefined value int i2 = int() ...

  9. STL笔记(4)关于erase,remove

    STL笔记(4)关于erase,remove 你要erase的元素很容易识别.它们是从区间的“新逻辑终点”开始持续到区间真的终点的原来区间的元素.要除去那些元素,你要做的所有事情就是用那两个迭代器调用 ...

随机推荐

  1. USB2.0学习笔记连载(四):安装Cypress官网套件

    上一篇博客大概讲了一下USB通用驱动程序的解析.笔者使用Cypress官网给定的资料去完成USB驱动开发.官网资料地址:http://www.cypress.com/?rID=14321 下载如下图的 ...

  2. 关于VS2008和VS2013中字体的选择

    我这学期上ASP.NET的课,用C#语言,配合VS2008.自己课余在研究手机游戏的开发,用的是C++语言,配合VS2013.这两种开发环境我自己试过好多字体,后来感觉适合我自己的应该是以下这两种: ...

  3. SAP 物料移动类型查询表

    Goods movement w/o referenceB Goods movement for purchase orderF Goods movement for production order ...

  4. Batch normalization:accelerating deep network training by reducing internal covariate shift的笔记

    说实话,这篇paper看了很久,,到现在对里面的一些东西还不是很好的理解. 下面是我的理解,当同行看到的话,留言交流交流啊!!!!! 这篇文章的中心点:围绕着如何降低  internal covari ...

  5. python3 post方式上传文件。

    借助第三方库:Requests 其官网地址:   http://python-requests.org       官网上写的安装方式:http://docs.python-requests.org/ ...

  6. MySQL中mysqldump导出数据的使用

    mysqldump常用参数说明: 帮助使用:mysqldump --help -A, --all-databases 导出全部数据库 mysqldump -uroot -p –A > /tmp/ ...

  7. XAMPP permissions on Mac OS X

    $ cd /Applications $ XAMPP/ 注意: 改变的是XAMPP目录,而不是htdocs ref: http://stackoverflow.com/questions/904697 ...

  8. C# 当前目录你了解多少

    C#中获取程序运行的当前目录的方法有多种,本文为大家提供三种方式,并作简单分析,有需要的朋友,可以参考下. 在C#中,有很多方式可以获取程序运行的当前目录,常见的方式有: .string strPat ...

  9. 视锥体(frustum)裁剪

    原文地址:http://www.linuxgraphics.cn/graphics/opengl_view_frustum_culling.html 背景 视锥体(frustum),是指场景中摄像机的 ...

  10. UIView - CAGradientLayer

    CAGradientLayer *layer = [[CAGradientLayer alloc] init]; layer.frame = self.bounds; //渐变转折点 layer.lo ...