Strings, Vectors, and Arrays

Namespace using Declarations

A using declaration lets us use a name from a namespace without qualifying the name with a namespace_name:: prefix. A using declaration has the form using namespace::name;

1
2
using std::cin;
using std::cout;

Library string Type

1
2
#include <string>
using std::string

初始化 string:

1
2
3
4
string s1; // default initialization; s1 is the empty string
string s2 = s1; // s2 is a copy of s1
string s3 = "hiya"; // s3 is a copy of the string literal
string s4(10, ’c’); // s4 is cccccccccc

直接初始化与拷贝初始化

expression description
string s1 Default initialization; s1 is the empty string.
string s2(s1) s2 is a copy of s1.
string s2 = s1 Equivalent to s2(s1), s2 is a copy of s1.
string s3(“value”) s3 is a copy of the string literal, not including the null.
string s3 = “value” Equivalent to s3(“value”), s3 is a copy of the string literal.
string s4(n, ’c’) Initialize s4 with n copies of the character ’c’.

Operations on string

expression description
os << s Writes s onto output stream os. Returns os.
is >> s Reads whitespace-separated string from is into s. Returns is.
getline(is, s) Reads a line of input from is into s. Returns is.
s.empty() Returns true if s is empty; otherwise returns false.
s.size() Returns the number of characters in s.
s[n] Returns a reference to the char at position n in s; positions start at 0.
s1 + s2 Returns a string that is the concatenation of s1 and s2.
s1 = s2 Replaces characters in s1 with a copy of s2.
s1 == s2 The strings s1 and s2 are equal if they contain the same characters.
s1 != s2 Equality is case-sensitive.
<, <=, >, >= Comparisons are case-sensitive and use dictionary ordering.

The string input operator reads and discards any leading whitespace (e.g., spaces, newlines, tabs). It then reads characters until the next whitespace character is encountered.

用 cin 读取输入流,向 string 中写入内容时,会消费并丢弃输入流开头的空格(也包括换行、Tab),只要输入流中的当前字符不是空白,就读取它,直到遇到第一个空白,这个空白不会被消费。

读取未知个数的字符串:

1
2
3
4
5
6
7
int main()
{
string word;
while (cin >> word) // read until end-of-file
cout << word << endl; // write each word followed by a new line
return 0;
}

The getline function takes an input stream and a string. This function reads the given stream up to and including the first newline and stores what it read—not including the newline—in its string argument. After getline sees a newline, even if it is the first character in the input, it stops reading and returns. If the first character in the input is a newline, then the resulting string is the empty string.

如果不想忽略输入中的空白字符,可以用 getline,能够一直读取,直到遇到换行符。输入流中的换行符会被读取,然后被丢弃,不会被存入 getline 第二个变量中。只读 \n,不存 \n

1
2
3
4
5
6
7
8
int main()
{
string line;
// read input a line at a time until end-of-file
while (getline(cin, line))
cout << line << endl;
return 0;
}

s.size() 的类型是 string::size_type,是一个无符号整数,利用自定义类型是为了做到与机器平台无关。

不能相加两个字符字面量,string 对象和字面量混用时,每个加号左右至少要有一个 string 对象:

When we mix strings and string or character literals, at least one operand to each + operator must be of string type:

1
2
3
4
string s4 = s1 + ", "; // ok: adding a string and a literal
string s5 = "hello" + ", "; // error: no string operand
string s6 = s1 + ", " + "world"; // ok: each + has a string operand
string s7 = "hello" + ", " + s2; // error: can’t add string literals

C++ 中,头文件尽量用 cname 而不是 name.h

In particular, the names defined in the cname headers are defined inside the std namespace, whereas those defined in the .h versions are not.

range-for statement

1
2
for (declaration : expression)
statement

On each iteration, the next character in str will be copied into c.

1
2
3
4
string s1 = "123";
for(auto c: s) {

}

Using a Range for to Change the Characters in a string

If we want to change the value of the characters in a string, wemust define the loop variable as a reference type.

1
2
3
4
5
string s("Hello World!!!");
// convert s to uppercase
for (auto &c : s) // for every char in s (note: c is a reference)
c = toupper(c); // c is a reference, so the assignment changes the char in s
cout << s << endl;

On each iteration, c refers to the next character in s.

Library vector Type

expression description
vector v1 vector that holds objects of type T. Default initialization;v1 is empty.
vector v2(v1) v2 has a copy of each element in v1.
vector v2 = v1 Equivalent to v2(v1), v2 is a copy of the elements in v1.
vector v3(n, val) v3 has n elements with value val.
vector v4(n) v4 has n copies of a value-initialized object.
vector v5{a,b,c . . .} v5 has as many elements as there are initializers; elements are initialized by corresponding initializers.
vector v5 = {a,b,c . . . } Equivalent to v5{a,b,c . . . }.

When we use parentheses, we are saying that the values we supply are to be used to construct the object.

When we use curly braces, {…}, we’re saying that, if possible, we want to list initialize the object. That is, if there is a way to use the values inside the curly braces as a list of element initializers, the class will do so. Only if it is not possible to list initialize the object will the other ways to initialize the object be considered.

使用大括号的时候,编译器会检查能否用大括号的内容做列表初始化,如果不能,编译器将会用里面的值作为构造参数来构建对象。换句话说,写到大括号里的内容,不一定全部都被当做列表初始化,也可能会被当做构造参数。

1
2
3
4
vector<string> v5{"hi"}; // list initialization: v5 has one element
vector<string> v6("hi"); // error: can’t construct a vector from a string literal
vector<string> v7{10}; // v7 has ten default-initialized elements
vector<string> v8{10, "hi"}; // v8 has ten elements with value "hi"
expression description
v.empty() Returns true if v is empty; otherwise returns false.
v.size() Returns the number of elements in v.
v.push_back(t) Adds an element with value t to end of v.
v[n] Returns a reference to the element at position n in v.
v1 = v2 Replaces the elements in v1 with a copy of the elements in v2.
v1 = {a,b,c . . . } Replaces the elements in v1 with a copy of the elements in the comma-separated list.
v1 == v2 v1 and v2 are equal if they have the same number of elements and each
v1 != v2 element in v1 is equal to the corresponding element in v2.
<, <=, >, >= Have their normal meanings using dictionary ordering.

The subscript operator on vector (and string) fetches an existing element; it does not add an element.

迭代器

大多数迭代器没有定义比较运算符,只有 = 和 !=

迭代器使用 ++ 从一个元素移动到下一个元素

我们可以对迭代器解除引用,获取迭代器指示元素的值

As with pointers, an iteratormay be valid or invalid. A valid iterator either denotes an element or denotes a position one past the last element in a container. All other iterator values are invalid.

Types that have iterators have members that return iterators. In particular, these types have members named begin and end.

1
2
3
// the compiler determines the type of b and e; see § 2.5.2 (p. 68)
// b denotes the first element and e denotes one past the last element in v
auto b = v.begin(), e = v.end(); // b and e have the same type

The iterator returned by end is an iterator positioned “one past the end” of the associated container (or string).

The iterator returned by end is often referred to as the off-the-end iterator or abbreviated as “the end iterator.”

We can compare two valid iterators using == or !=. Iterators are equal if they denote the same element or if they are both off-the-end iterators for the same container. Otherwise, they are unequal.

如果两个迭代器知识的元素相同,或者都指向 off-the-end,这两个迭代器相等,其他情况他们不相等。

The library types that have iterators define types named iterator and const_iterator that represent actual iterator types:

1
2
3
4
vector<int>::iterator it; // it can read and write vector<int> elements
string::iterator it2; // it2 can read and write characters in a string
vector<int>::const_iterator it3; // it3 can read but not write elements
string::const_iterator it4; // it4 can read but not write characters

如果一个容器对象是常量,那么通过 v.begin()v.end() 获取到的迭代器也是 const_iterator

还可以通过 v.cbegin()v.cend() 显示获取常量迭代器。

常量迭代器只能读取不能修改所指示的内容。

使用迭代器的循环里面不要改变容器的大小,否则会造成迭代器失效。

解除引用并访问成员

(*it).empty() 注意需要括号。

Iterator Arithmetic

可以对两个迭代器做减法,返回的是两个迭代器之间的距离,类型是 difference_type。This type is signed, because subtraction might have a negative result.

Arrays

Arrays have fixed size; we cannot add elements to an array.

Performance is better.

Defining and Initializing Built-in Arrays

An array declarator has the form a[d].

The number of elements in an array is part of the array’s type.

As a result, the dimension must be known at compile time, which means that the dimension must be a constant expression.

As with vector, arrays hold objects. Thus, there are no arrays of references.

Explicitly Initializing Array Elements

If we omit the dimension, the compiler infers it from the number of initializers.

If we specify a dimension, the number of initializers must not exceed the specified size.

If the dimension is greater than the number of initializers, the initializers are used for the first elements and any remaining elements are value initialized.

Character Arrays are Special

Character arrays have an additional form of initialization: We can initialize such arrays from a string literal.

string literal ends with a null character.

1
2
3
4
char a1[] = {’C’, ’+’, ’+’}; // list initialization, no null
char a2[] = {’C’, ’+’, ’+’, ’\0’}; // list initialization, explicit null
char a3[] = "C++"; // null terminator added automatically
const char a4[6] = "Daniel"; // error: no space for the null!

No Copy or Assignment

We cannot initialize an array as a copy of another array, nor is it legal to assign one array to another:

1
2
3
int a[] = {0, 1, 2}; // array of three ints
int a2[] = a; // error: cannot initialize one array with another
a2 = a; // error: cannot assign one array to another