三十一、容器的非成员函数swap
在新标准库中,容器既提供成员函数版本的swap,也提供非成员版本的swap,而早期标准库版本只提供成员函数版本的swap。非成员版本的swap在泛形编程中是非常重要的,统一使用非成员版本的swap是一个好习惯。
三十二、容器insert成员的返回类型
在新标准下,接受元素个数或范围的insert版本返回指向第一个新加入元素的迭代器。(在旧版本中,这些操作返回void。)如果范围为空,不插入任何元素,insert操作会将第一个参数返回。
三十三、容器的emplace成员的返回类型
新标准引入了三个新成员:emplace_front、emplace和emplace_back,这些操作构造而不是拷贝元素。这些操作分别对应push_front、push和push_back,允许我们把元素放置在容器头部、一个指定位置之前或者容器尾部。当调用push或insert成员函数时,我们将元素类型的对象传递给它们,这些对象被拷贝到容器中。而当我们调用一个emplace成员函数时,则是将参数传递给元素类型的构造函数。
emplace成员使用这些参数在容器管理的内存空间中直接构造元素。例如,假定c保存Sales_data元素:
//在c的末尾构造一个Sales_data对象
//使用三个参数的Sales_data构造函数
c.emplace_back("978-059", 25, 15.99);
//错误,没有接受3个参数的push_back版本
c.push_back("978-059", 25, 15.99);
//正确,创建一个临时的Sales_data对象传递给push_back
c.push_back(Sales_data("978-059", 25, 15.99));
其中对emplace_back的调用和第二个push_back调用都会创建新的Sales_data对象。在调用emplace_back时,会在容器管理的内存空间中直接创建对象。而调用push_back则会创建一个局部临时对象,并将其压入容器中。
三十四、shrink_to_fit
在新标准库中,我们可以调用shrink_to_fit来要求deque、vector和string退回不需要的内存空间,此函数指出我们不再需要任何多余的内存空间。但是,具体的实现可以选择忽略此请求。也就是说,使用shrink_to_fit也并不保证一定退回内存空间。
三十五、string的数值转换函数
新标准引入了多个函数,可以实现数值数据与标准库string之间的转换:
int i = 42;
string s = to_string(i); //将整数i转换为字符表示形式
double d = stod(s); //将字符串s转换为浮点数
三十六、关联容器的列表初始化
当定义一个map时,必须既指明关键字类型又指明值类型;而定义一个set时,只需指明关键字类型,因为set中没有值。每个关联容器都定义了一个默认构造函数,它创建一个指定类型的空容器。我们也可以将关联容器初始化为另一个同类型容器的拷贝,或是从一个值范围来初始化关联容器,只要这些值能转化为容器所需类型就可以。在新标准下,我们也可以对关联容器进行值初始化:
map<string, size_t> word_count; //空容器
//列表初始化
set<string> exclude = {"the", "but", "and", "or", "an", "a"};
//三个元素:author将姓映射成名
map<string, string> authors = { {"Joyce", "James"},
{"Austen", "Jane"}
{"Dickens", "Charles"}};
与以往一样,初始化器必须能转换为容器中元素的类型。对于set,元素类型就是关键字类型。
当初始化一个map时,必须提供关键字类型和值类型。我们将每个关键字-值对包围在花括号中:{key, value}来指出他们一起构成了map中的一个元素。在每个花括号中,关键字是第一个元素,值是第二个。
三十七、pair的列表初始化
对一个map进行inset操作时,必须记住元素类型是pair。通常,对于想要插入的数据,并没有一个现成的pair对象。可以在insert的参数列表中创建一个pair:
word_count.inset(word,1);
word_count.inset(make_pair(word, 1));
word_count.inset(pair<string, size_t>(word, 1));
word_count.inset(map<string, size_t>::value_type(word, 1));
在新标准下,创建一个pari最简单的方法是在参数列表中使用花括号初始化。也可以调用make_pair或显式构造pair。最后一个insert中调用的参数:
map<string, size_t>::value_type(s, 1)
构造一个恰当的pair类型,并构造一个新对象,插入到map中。
三十八、智能指针
为了更容易(同时也更安全)地使用动态内存,新的标准库提供了两种指针指针(smart pointer)类型来管理动态对象。智能指针的行为类似常规指针,重要的区别是它复制自动释放所指向的对象。新标准库提供的这两种智能指针的区别在于管理底层指针的方式:shared_ptr允许多个指针指向同一个对象,unique_ptr则“独占”所指向的对象。标准库还定义了一个名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象。这三种类型都定义在memory头文件中。
三十九、动态分配对象的列表初始化
我们可以使用直接初始化方式来初始化一个动态分配的对象。我们可以使用传统的构造方式(使用圆括号),在新标准下,也可以使用列表初始化(使用花括号):
int *pi = new int(1024); //pi指向的对象的值为1024
string *ps = new string(10, '9'); //*ps为"9999999999"
//vector有10个元素,值依次从0到9
vector<int> *pv = new vector<int>{0,1,2,3,4,5,6,7,8,9};
也可以对动态分配的对象进行值初始化,只需在类型名后跟一对空括号即可:
string *ps1 = new string; //默认初始化为空string
string *ps = new string(); //值初始化为空string
int *pi1 = new int; //默认初始化;*pi1的值未定义
int *pi2 = new int(); //值初始化为0;*pi2为0
四十、auto和动态分配
如果我们提供了一个括号包围的初始化器,就可以使用auto从此初始化器来推断我们想要分配的对象的类型。但是,由于编译器要用初始化器的类型来推断要分配的类型,只有当括号中仅有单一初始化器时才可以使用auto:
auto p1 = new auto(obj); //p指向一个与obj类型相同的对象,该对象用obj进行初始化
auto p2 = new auto(a, b, c);//错误,括号中只能由单个初始化器。