Team LiB
Previous Section Next Section

9.1. Defining a Sequential Container

9.1. 顺序容器的定义

We already know a fair bit about how to use the sequential containers based on what we covered in Section 3.3 (p. 90). To define a container object, we must include its associated header file, which is one of

第 3.3 节中,我们已经了解了一些使用顺序容器类型的知识。为了定义一个容器类型的对象,必须先包含相关的头文件,即下列头文件之一:

     #include <vector>
     #include <list>
     #include <deque>

Each of the containers is a class template (Section 3.3, p. 90). To define a particular kind of container, we name the container followed by angle brackets that enclose the type of the elements the container will hold:

所有的容器都是类模板(第 3.3 节)。要定义某种特殊的容器,必须在容器名后加一对尖括号,尖括号里面提供容器中存放的元素的类型:

     vector<string>    svec;       // empty vector that can hold strings
     list<int>         ilist;      // empty list that can hold ints
     deque<Sales_item> items;      // empty deque that holds Sales_items

Each container defines a default constructor that creates an empty container of the speicfied type. Recall that a default constructor takes no arguments.

所有容器类型都定义了默认构造函数,用于创建指定类型的空容器对象。默认构造函数不带参数。

For reasons that shall become clear shortly, the most commonly used container constructor is the default constructor. In most programs, using the default constructor gives the best run-time performance and makes using the container easier.

为了使程序更清晰、简短,容器类型最常用的构造函数是默认构造函数。在大多数的程序中,使用默认构造函数能达到最佳运行时性能,并且使容器更容易使用。



9.1.1. Initializing Container Elements

9.1.1. 容器元素的初始化

In addition to defining a default constructor, each container type also supports constructors that allow us to specify initial element values.

除了默认构造函数,容器类型还提供其他的构造函数,使程序员可以指定元素初值,见表 9.2

Table 9.2. Container Constructors
表 9.2. 容器构造函数

C<T> c;

Create an empty container named c. C is a container name, such as vector, and T is the element type, such as int or string. Valid for all containers.

创建一个名为 c 的空容器。C 是容器类型名,如 vectorT 是元素类型,如 intstring 适用于所有容器。

C c(c2);

Create c as a copy of container c2; c and c2 must be the same container type and hold values of the same type. Valid for all containers.

创建容器 c2 的副本 ccc2 必须具有相同的容器类型,并存放相同类型的元素。适用于所有容器。

C c(b, e);

Create c with a copy of the elements from the range denoted by iterators b and e. Valid for all containers.

创建 c,其元素是迭代器 be 标示的范围内元素的副本。适用于所有容器。

C c(n, t);

Create c with n elements, each with value t, which must be a value of the element type of C or a type convertible to that type.

n 个值为 t 的元素创建容器 c,其中值 t 必须是容器类型 C 的元素类型的值,或者是可转换为该类型的值。

Sequential containers only.

只适用于顺序容器

C c(n);

Create c with n value-initialized (Section 3.3.1, p. 92) elements.

创建有 n 个值初始化(第 3.3.1 节)(value-initialized)元素的容器 c

Sequential containers only.

只适用于顺序容器


Intializing a Container as a Copy of Another Container
将一个容器初始化为另一个容器的副本

When we initialize a sequential container using any constructor other than the default constructor, we must indicate how many elements the container will have. We must also supply initial values for those elements. One way to specify both the size and element values is to initialize a new container as a copy of an existing container of the same type:

当不使用默认构造函数,而是用其他构造函数初始化顺序容器时,必须指出该容器有多少个元素,并提供这些元素的初值。同时指定元素个数和初值的一个方法是将新创建的容器初始化为一个同类型的已存在容器的副本:

     vector<int> ivec;
     vector<int> ivec2(ivec);   // ok: ivec is vector<int>
     list<int>   ilist(ivec);   // error: ivec is not list<int>
     vector<double> dvec(ivec); // error: ivec holds int not double

When we copy one container into another, the types must match exactly: The container type and element type must be the same.

将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。



Initializing as a Copy of a Range of Elements
初始化为一段元素的副本

Although we cannot copy the elements from one kind of container to another directly, we can do so indirectly by passing a pair of iterators (Section 3.4, p. 95). When we use iterators, there is no requirement that the container types be identical. The element types in the containers can differ as long as they are compatible. It must be possible to convert the element we copy into the type held by the container we are constructing.

尽管不能直接将一种容器内的元素复制给另一种容器,但系统允许通过传递一对迭代器(第 3.4 节)间接实现该实现该功能。使用迭代器时,不要求容器类型相同。容器内的元素类型也可以不相同,只要它们相互兼容,能够将要复制的元素转换为所构建的新容器的元素类型,即可实现复制。

The iterators denote a range of elements that we want to copy. These elements are used to initialize the elements of the new container. The iterators mark the first and one past the last element to be copied. We can use this form of initialization to copy a container that we could not copy directly. More importantly, we can use it to copy only a subsequence of the other container:

迭代器标记了要复制的元素范围,这些元素用于初始化新容器的元素。迭代器标记出要复制的第一个元素和最后一个元素。采用这种初始化形式可复制不能直接复制的容器。更重要的是,可以实现复制其他容器的一个子序列:

     // initialize slist with copy of each element of svec
     list<string> slist(svec.begin(), svec.end());

     // find midpoint in the vector
     vector<string>::iterator mid = svec.begin() + svec.size()/2;

     // initialize front with first half of svec: The elements up to but not including *mid
     deque<string> front(svec.begin(), mid);
     // initialize back with second half of svec: The elements *mid through end of svec
     deque<string> back(mid, svec.end());

Recall that pointers are iterators, so it should not be surprising that we can initialize a container from a pair of pointers into a built-in array:

回顾一下指针,我们知道指针就是迭代器,因此允许通过使用内置数组中的一对指针初始化容器也就不奇怪了:

     char *words[] = {"stately", "plump", "buck", "mulligan"};

     // calculate how many elements in words
     size_t words_size = sizeof(words)/sizeof(char *);

     // use entire array to initialize words2
     list<string> words2(words, words + words_size);

Here we use sizeof (Section 5.8, p. 167) to calculate the size of the array. We add that size to a pointer to the first element to get a pointer to a location one past the end of the array. The initializers for words2 are a pointer to the first element in words and a second pointer one past the last element in that array. The second pointer serves as a stopping condition; the location it addresses is not included in the elements to be copied.

这里,使用 sizeof5.8 节)计算数组的长度。将数组长度加到指向第一个元素的指针上就可以得到指向超出数组末端的下一位置的指针。通过指向第一个元素的指针 words 和指向数组中最后一个元素的下一位置的指针,实现了 words2 的初始化。其中第二个指针提供停止复制的条件,其所指向的位置上存放的元素并没有复制。

Allocating and Initializing a Specified Number of Elements
分配和初始化指定数目的元素

When creating a sequential container, we may specify an explicit size and an (optional) initializer to use for the elements. The size can be either a constant or non-constant expression. The element initializer must be a valid value that can be used to initialize an object of the element type:

创建顺序容器时,可显式指定容器大小和一个(可选的)元素初始化式。容器大小可以是常量或非常量表达式,元素初始化则必须是可用于初始化其元素类型的对象的值:

     const list<int>::size_type list_size = 64;
     list<string> slist(list_size, "eh?"); // 64 strings, each is eh?

This code initializes slist to have 64 elements, each with the value eh?.

这段代码表示 slist 含有 64 个元素,每个元素都被初始化为“eh?”字符串。

As an alternative to specifying the number of elements and an element initializer, we can also specify only the size:

创建容器时,除了指定元素个数,还可选择是否提供元素初始化式。我们也可以只指定容器大小:

list<int> ilist(list_size); // 64 elements, each initialized to 0
// svec has as many elements as the return value from get_word_count
extern unsigned get_word_count(const string &file_name);
vector<string> svec(get_word_count("Chimera"));

When we do not supply an element initializer, the library generates a value-initialized (Section 3.3.1, p. 92) one for us. To use this form of initialization, the element type must either be a built-in or compound type or be a class type that has a default constructor. If the element type does not have a default constructor, then an explicit element initializer must be specified.

不提供元素初始化式时,标准库将为该容器实现值初始化(3.3.1&nbps;节)。采用这种类型的初始化,元素类型必须是内置或复合类型,或者是提供了默认构造函数的类类型。如果元素类型没有默认构造函数,则必须显式指定其元素初始化式。

The constructors that take a size are valid only for sequential containers; they are not supported for the associative containers,

接受容器大小做形参的构造函数只适用于顺序容器,而关联容器不支持这种初始化。



Exercises Section 9.1.1

Exercise 9.1:

Explain the following initializations. Indicate if any are in error, and if so, why.

解释下列初始化,指出哪些是错误的,为什么?

     int ia[7] = { 0, 1, 1, 2, 3, 5, 8 };
     string sa[6] = {
         "Fort Sumter", "Manassas", "Perryville",
         "Vicksburg", "Meridian", "Chancellorsville" };
     (a) vector<string> svec(sa, sa+6);
     (b) list<int> ilist( ia+4, ia+6);
     (c) vector<int> ivec(ia, ia+8);
     (d) list<string> slist(sa+6, sa);

Exercise 9.2:

Show an example of each of the four ways to create and initialize a vector. Explain what values each vector contains.

创建和初始化一个 vector 对象有 4 种方式,为每种方式提供一个例子,并解释每个例子生成的 vector 对象包含什么值。

Exercise 9.3:

Explain the differences between the constructor that takes a container to copy and the constructor that takes two iterators.

解释复制容器对象的构造函数和使用两个迭代器的构造函数之间的差别。


9.1.2. Constraints on Types that a Container Can Hold

9.1.2. 容器内元素的类型约束

While most types can be used as the element type of a container, there are two constraints that element types must meet:

C++ 语言中,大多数类型都可用作容器的元素类型。容器元素类型必须满足以下两个约束:

  • The element type must support assignment.

    元素类型必须支持赋值运算。

  • We must be able to copy objects of the element type.

    元素类型的对象必须可以复制。

There are additional constraints on the types used as the key in an associative container, which we'll cover in Chapter 10.

此外,关联容器的键类型还需满足其他的约束,我们将在第十章介绍相关内容。

Most types meet these minimal element type requirements. All of the built-in or compound types, with the exception of references, can be used as the element type. References do not support assignment in its ordinary meaning, so we cannot have containers of references.

大多数类型满足上述最低限度的元素类型要求。除了引用类型外,所有内置或复合类型都可用做元素类型。引用不支持一般意义的赋值运算,因此没有元素是引用类型的容器。

With the exception of the IO library types (and the auto_ptr type, which we cover in Section 17.1.9 (p. 702)), all the library types are valid container element types. In particular, containers themselves satisfy these requirements. We can define containers with elements that are themselves containers. Our Sales_item type also satisifes these requirements.

除输入输出(IO)标准库类型(以及第 17.1.9 节介绍的 auto_ptr 类型)之外,所有其他标准库类型都是有效的容器元素类型。特别地,容器本身也满足上述要求,因此,可以定义元素本身就是容器类型的容器。Sales_item 类型也满足上述要求。

The IO library types do not support copy or assignment. Therefore, we cannot have a container that holds objects of the IO types.

IO 库类型不支持复制或赋值。因此,不能创建存放 IO 类型对象的容器。

Container Operations May Impose Additional Requirements
容器操作的特殊要求

The requirement to support copy and assignment is the minimal requirement on element types. In addition, some container operations impose additional requirements on the element type. If the element type doesn't support the additional requirement, then we cannot perform that operation: We can define a container of that type but may not use that particular operation.

支持复制和赋值功能是容器元素类型的最低要求。此外,一些容器操作对元素类型还有特殊要求。如果元素类型不支持这些特殊要求,则相关的容器操作就不能执行:我们可以定义该类型的容器,但不能使用某些特定的操作。

One example of an operation that imposes a type constraint is the constructors that take a single initializer that specifies the size of the container. If our container holds objects of a class type, then we can use this constructor only if the element type has a default constructor. Most types do have a default constructor, although there are some classes that do not. As an example, assume that Foo is a class that does not define a default constructor but that does have a constructor that takes an int argument. Now, consider the following declarations:

其中一种需外加类型要求的容器操作是指定容器大小并提供单个初始化式的构造函数。如果容器存储类类型的对象,那么只有当其元素类型提供默认构造函数时,容器才能使用这种构造函数。尽管有一些类没有提供默认构造函数,但大多数类类型都会有。例如,假设类 Foo 没有默认构造函数,但提供了需要一个 int 型形参的构造函数。现在,考虑下面的声明:

     vector<Foo> empty;     // ok: no need for element default constructor
     vector<Foo> bad(10);   // error: no default constructor for Foo
     vector<Foo> ok(10, 1); // ok: each element initialized to 1

We can define an empty container to hold Foo objects, but we can define one of a given size only if we also specify an initializer for each element.

我们定义一个存放 Foo 类型对象的空容器,但是,只有在同时指定每个元素的初始化式时,才能使用给定容器大小的构造函数来创建同类型的容器对象。

As we describe the container operations, we'll note the constraints, if any, that each container operation places on the element type.

在描述容器操作时,我们应该留意(如果有的话)每个操作对元素类型的约束。

Containers of Containers
容器的容器

Because the containers meet the constraints on element types, we can define a container whose element type is itself a container type. For example, we might define lines as a vector whose elements are a vector of strings:

因为容器受容器元素类型的约束,所以可定义元素是容器类型的容器。例如,可以定义 vector 类型的容器 lines,其元素为 string 类型的 vector 对象:

     // note spacing: use ">>" not ">>" when specifying a container element type
     vector< vector<string> > lines; // vector of vectors

Note the spacing used when specifying a container element type as a container:

注意,在指定容器元素为容器类型时,必须如下使用空格:

     vector< vector<string> > lines; // ok: space required between close >
     vector< vector<string>> lines; // error: >> treated as shift operator

必须用空格隔开两个相邻的 > 符号,以示这是两个分开的符号,否则,系统会认为 >> 是单个符号,为右移操作符,并导致编译时错误。

We must separate the two closing > symbols with a space to indicate that these two characters represent two symbols. Without the space, >> is treated as a single symbol, the right shift operator, and results in a compile-time error.



Exercises Section 9.1.2

Exercise 9.4:

Define a list that holds elements that are deques that hold ints.

定义一个 list 对象来存储 deque 对象里的元素,该 deque 对象存放 int 型元素。

Exercise 9.5:

Why can we not have containers that hold iostream objects?

为什么我们不可以使用容器来存储 iostream 对象?

Exercise 9.6:

Given a class type named Foo that does not define a default constructor but does define a constructor that takes int values, define a list of Foo that holds 10 elements.

假设有一个名为 Foo 的类,这个类没有定义默认构造函数,但提供了需要一个 int 型参数的构造函数,定义一个存放 Foolist 对象,该对象有 10 个元素。


    Team LiB
    Previous Section Next Section