C++ Primer 第三章 Strings, Vectors, and Arrays

本文深入介绍了C++ Primer中关于字符串、向量和数组的使用,包括命名空间的using声明、标准库中的string类型及其操作如初始化、读写、比较、赋值和连接等。接着探讨了vector类型,讲解了如何定义、初始化、添加元素以及高效扩容。此外,还讨论了迭代器的概念和用法,以及数组的定义、初始化和指针操作。文章通过实例展示了多维数组的使用和范围基础for循环在处理数组元素时的应用。
摘要由CSDN通过智能技术生成

3.1. Namespace using Declarations

Namespaces

  • A using declaration lets us use a name from a namespace without qualifying the name with a namespace_name :: prefix.
using namespace::name;
  • Code inside headers ordinarily should not use using declarations.

3.2. Library string Type

  • A string is a variable-length sequence of characters.

  • To use the string type, we must include the string header.

  • Because it is part of the library, string is defined in the std namespace.

#include <string>
using std::string;

3.2.1. Defining and Initializing strings

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

在这里插入图片描述

Direct and Copy Forms of Initialization

  • When we initialize a variable using =, we are asking the compiler to copy initialize the object by copying the initializer on the right-hand side into the object being created.

  • Otherwise, when we omit the =, we use direct initialization.

  • When we have a single initializer, we can use either the direct or copy form of initialization.

  • When we initialize a variable from more than one value, such as in the initialization of s4 above, we must use the direct form of initialization:

string s5 = "hiya"; // copy initialization
string s6("hiya"); // direct initialization
string s7(10, 'c'); // direct initialization; s7 is cccccccccc
string s8 = string(10, 'c'); // copy initialization; s8 is cccccccccc

Difference between direct and copy Initialization: Is there a difference between copy initialization and direct initialization?

3.2.2. Operations on strings

1

Reading and Writing strings

// Note: #include and using declarations must be added to compile this code
int main()
{
string s; // empty string
cin >> s; // read a whitespace-separated string into s
cout << s << endl; // write s to the output
return 0;
}
  • The string input operator reads and discards any leading whitespace (e.g., spaces, newlines, tabs).

  • Like the input and output operations on the built-in types, the string operators return their left-hand operand as their result.

string s1, s2;
cin >> s1 >> s2; // read first input into s1, second into s2
cout << s1 << s2 << endl; // write both strings

Reading an Unknown Number of strings:

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;
}

Using getline to Read an Entire Line

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.

If the first character in the input is a newline, then the resulting string is the empty string.

The string empty and size Operations

std::string::size

// read input a line at a time and discard blank lines
while (getline(cin, line))
if (!line.empty())
cout << line << endl;
string line;
// read input a line at a time and print lines that are longer than 80 characters
while (getline(cin, line))
if (line.size() > 80)
cout << line << endl;

Return: size returns a string::size_type value.

Although we don’t know the precise type of string::size_type, we do know that it is an unsigned type big enough to hold the size of any string.

Admittedly, it can be tedious to type string::size_type. Under the new standard, we can ask the compiler to provide the appropriate type by using auto or decltype.

auto len = line.size(); // len has type string::size_type

sizelength 相同,返回字符串长度(不包括末尾的空字符)。

Comparing strings

  • The equality operators (== and !=) test whether two strings are equal or unequal, respectively.

  • The relational operators <, <=, >, >= test whether one string is less than, less than or equal to, greater than, or greater than or equal to another.

  • These operators use the same strategy as a (case-sensitive) dictionary:

    • If two strings have different lengths and if every character in the shorter string is equal to the corresponding character of the longer string, then the shorter string is less than the longer one.

    • If any characters at corresponding positions in the two strings differ, then the result of the string comparison is the result of comparing the first character at which the strings differ.

Assignment for strings

string st1(10, 'c'), st2; // st1 is cccccccccc; st2 is an empty string
st1 = st2; // assignment: replace contents of st1 with a copy of st2
// both st1 and st2 are now the empty string

Adding Two strings

string s1 = "hello, ", s2 = "world\n";
string s3 = s1 + s2; // s3 is hello, world\n
s1 += s2; // equivalent to s1 = s1 + s2

Adding Literals and strings

string s1 = "hello", s2 = "world"; // no punctuation in s1 or s2
string s3 = s1 + ", " + s2 + '\n';

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

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
string s6 = (s1 + ", ") + "world";

The subexpression s1 + ", " returns a string, which forms the left-hand operand of the second + operator. It is as if we had written:

string tmp = s1 + ", "; // ok: + has a string operand
s6 = tmp + "world"; // ok: + has a string operand
string s7 = ("hello" + ", ") + s2; // error: can't add string literals

3.2.3. Dealing with the Characters in a string

cctype Functions:
cctype

Range-Based for

The body of a range for must not change the size of the sequence over which it is iterating.

for (declaration : expression)
statement
string str("some string");
// print the characters in str one character to a line
for (auto c : str) // for every char in str
	cout << c << endl; // print the current character followed by a newline

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

string s("Hello World!!!");
// punct_cnt has the same type that s.size returns; see § 2.5.3 (p. 70)
decltype(s.size()) punct_cnt = 0;
// count the number of punctuation characters in s
for (auto c : s) // for every char in s
	if (ispunct(c)) // if the character is punctuation
		++punct_cnt; // increment the punctuation counter

cout << punct_cnt
	 << " punctuation characters in " << s << endl;

Change the Characters in a string

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;

Processing Only Some Characters

if (!s.empty()) // make sure there's a character to print
	cout << s[0] << endl; // print the first character in s

3.3. Library vector Type

vector vs. list in STL
Standard Template Library Programmer’s Guide
C++ benchmark – std::vector VS std::list VS std::deque
c++ vector
STL Containers

  • A vector is a collection of objects, all of which have the same type.
  • A vector is a class template.
    Templates are not themselves functions or classes.
    Instead, they can be thought of as instructions to the compiler for generating classes or functions.
  • The process that the compiler uses to create classes or functions from templates is called instantiation.
vector<int> ivec; // ivec holds objects of type int
vector<Sales_item> Sales_vec; // holds Sales_items
vector<vector<string>> file; // vector whose elements are vectors

3.3.1. Defining and Initializing vectors

1

vector<int> ivec; // initially empty
// give ivec some values
vector<int> ivec2(ivec); // copy elements of ivec into ivec2
vector<int> ivec3 = ivec; // copy elements of ivec into ivec3
vector<string> svec(ivec2); // error: svec holds strings, not ints

List Initializing a vector

vector<string> articles = {"a", "an", "the"};

Creating a Specified Number of Elements

vector<int> ivec(10, -1); // ten int elements, each initialized to -1
vector<string> svec(10, "hi!"); // ten strings; each element is "hi!"

Value Initialization

vector<int> ivec(10); // ten elements, each initialized to 0
vector<string> svec(10); // ten elements, each an empty string

There are two restrictions on this form of initialization:

  1. Some classes require that we always supply an explicit initializer.
    If our vector holds objects of a type that we cannot default initialize, then we must supply an initial element value;

  2. When we supply an element count without also supplying an initial value, we must use the direct form of initialization.

List Initializer or Element Count

  • 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.

vector<int> v1(10); // v1 has ten elements with value 0
vector<int> v2{10}; // v2 has one element with value 10
vector<int> v3(10, 1); // v3 has ten elements with value 1
vector<int> v4{10, 1}; // v4 has two elements with values 10 and 1
  • On the other hand, if we use braces and there is no way to use the initializers to list initialize the object, then those values will be used to construct the object.
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"

Although we used braces on all but one of these definitions, only v5 is list initialized.

3.3.2. Adding Elements to a vector

vector<int> v2; // empty vector
for (int i = 0; i != 100; ++i)
	v2.push_back(i); // append sequential integers to v2
// at end of loop v2 has 100 elements, values 0 . . . 99
// read words from the standard input and store them as elements in a vector
string word;
vector<string> text; // empty vector
while (cin >> word) {
	text.push_back(word); // append word to text
}

vectors Grow Efficiently

If differing element values are needed, it is usually more efficient to define an empty vector and add elements as the values we need become known at run time.

3.3.3. Other vector Operations

1


To use size_type, we must name the type in which it is defined. A vector type always includes its element type:

vector<int>::size_type // ok
vector::size_type // error

Computing a vector Index

// count the number of grades by clusters of ten: 0--9, 10--19, . .. 90--99, 100
vector<unsigned> scores(11, 0); // 11 buckets, all initially 0
unsigned grade;
while (cin >> grade) { // read the grades
	if (grade <= 100) // handle only valid grades
		++scores[grade/10]; // increment the counter for the current cluster
}

Subscripting Does Not Add Elements

vector<int> ivec; // empty vector
for (decltype(ivec.size()) ix = 0; ix != 10; ++ix)
	ivec[ix] = ix; // disaster: ivec has no elements
for (decltype(ivec.size()) ix = 0; ix != 10; ++ix)
ivec.push_back(ix); // ok: adds a new element with value ix

3.4. Introducing Iterators

3.4.1. Using Iterators

// the compiler determines the type of b and e; 
// 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

Iterator Operations

1


string s("some string");
if (s.begin() != s.end()) { // make sure s is not empty
	auto it = s.begin(); // it denotes the first character in s
	*it = toupper(*it); // make that character uppercase
}

Iterator Types

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

The begin and end Operations

If the object is const, then begin and end return a const_iterator; if the object is not const, they return iterator:

vector<int> v;
const vector<int> cv;
auto it1 = v.begin(); // it1 has type vector<int>::iterator
auto it2 = cv.begin(); // it2 has type vector<int>::const_iterator
auto it3 = v.cbegin(); // it3 has type vector<int>::const_iterator

3.4.2. Iterator Arithmetic

1

We can also subtract two iterators so long as they refer to elements in, or one off the end of, the same vector or string.

The result is the distance between the iterators.

The result type is a signed integral type named difference_type.

A classic algorithm that uses iterator arithmetic is binary search.

// text must be sorted
// beg and end will denote the range we're searching
auto beg = text.begin(), end = text.end();
auto mid = text.begin() + (end - beg)/2; // original midpoint
// while there are still elements to look at and we haven't yet found sought
while (mid != end && *mid != sought) {
	if (sought < *mid) // is the element we want in the first half?
		end = mid; // if so, adjust the range to ignore the second half
	else // the element we want is in the second half
		beg = mid + 1; // start looking with the element just after mid
	
	mid = beg + (end - beg)/2; // new midpoint
}

3.5. Arrays

  • Like a vector, an array is a container of unnamed objects of a single type that we access by position.

  • Unlike a vector, arrays have fixed size; we cannot add elements to an array.

3.5.1. Defining and Initializing Built-in Arrays

  • The dimension must be known at compile time, which means that the dimension must be a constant expression.
unsigned cnt = 42; // not a constant expression
constexpr unsigned sz = 42; // constant expression
// constexpr see § 2.4.4 (p. 66)
int arr[10]; // array of ten ints
int *parr[sz]; // array of 42 pointers to int
string bad[cnt]; // error: cnt is not a constant expression
string strs[get_size()]; // ok if get_size is constexpr, error otherwise
string strs[3];
  • We cannot use auto to deduce the type from a list of initializers.

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

const unsigned sz = 3;
int ia1[sz] = {0,1,2}; // array of three ints with values 0, 1, 2
int a2[] = {0, 1, 2}; // an array of dimension 3
int a3[5] = {0, 1, 2}; // equivalent to a3[] = {0, 1, 2, 0, 0}
string a4[3] = {"hi", "bye"}; // same as a4[] = {"hi", "bye", ""}
int a5[2] = {0,1,2}; // error: too many initializers
int a2[] = {0, 1, 2}; // an array of three elements, not three-dimensional
  • Character arrays have an additional form of initialization: We can initialize such arrays from a string literal。
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!

The dimension of a1 is 3; the dimensions of a2 and a3 are both 4. The definition of a4 is in error.

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

Complicated Array Declarations

int *ptrs[10]; // ptrs is an array of ten pointers to int
int &refs[10] = /* ? */; // error: no arrays of references
int (*Parray)[10] = &arr; // Parray points to an array of ten ints
int (&arrRef)[10] = arr; // arrRef refers to an array of ten ints
int *(&arry)[10] = ptrs; // arry is a reference to an array of ten pointers

3.5.3. Pointers and Arrays

  • When we use an array as an initializer for a variable defined using auto, the deduced type is a pointer, not an array:
int ia[] = {0,1,2,3,4,5,6,7,8,9}; // ia is an array of ten ints
auto ia2(ia); // ia2 is an int* that points to the first element in ia
ia2 = 42; // error: ia2 is a pointer, and we can't assign an int to a pointer
  • It is worth noting that this conversion does not happen when we use decltype. The type returned by decltype(ia) is array of ten ints:
// ia3 is an array of ten ints
decltype(ia) ia3 = {0,1,2,3,4,5,6,7,8,9};
ia3 = p; // error: can't assign an int* to an array
ia3[4] = i; // ok: assigns the value of i to an element in ia3

The Library begin and end Functions

arrays are not class types, so these functions are not functions. Instead, they take an argument that is an array:

int ia[] = {0,1,2,3,4,5,6,7,8,9}; // ia is an array of ten ints
int *beg = begin(ia); // pointer to the first element in ia
int *last = end(ia); // pointer one past the last element in ia

begin returns a pointer to the first, and end returns a pointer one past the last element in the given array. These functions are defined in the iterator header.

Pointer Arithmetic

int *b = arr, *e = arr + sz;
while (b < e) {
	// use *b
	++b;
}
int i = 0, sz = 42;
int *p = &i, *e = &sz;
// undefined: p and e are unrelated; comparison is meaningless!
while (p < e)

 

Although the utility may be obscure at this point, it is worth noting that pointer arithmetic is also valid for null pointers and for pointers that point to an object that is not an array.

If p is a null pointer, we can add or subtract an integral constant expression whose value is 0 to p.

In the latter case, the pointers must point to the same object, or one past that object.

Subscripts and Pointers

int *p = &ia[2]; // p points to the element indexed by 2
int j = p[1]; // p[1] is equivalent to *(p + 1),
// p[1] is the same element as ia[3]
int k = p[-2]; // p[-2] is the same element as ia[0]

3.6. Multidimensional Arrays

Strictly speaking, there are no multidimensional arrays in C++. What are commonly referred to as multidimensional arrays are actually arrays of arrays.

Using a Range for with Multidimensional Arrays

constexpr size_t rowCnt = 3, colCnt = 4;
int ia[rowCnt][colCnt];
size_t cnt = 0;
for (auto &row : ia) // for every element in the outer array
	for (auto &col : row) { // for every element in the inner array
		col = cnt; // give this element the next value
		++cnt; // increment cnt
}

note
To use a multidimensional array in a range for, the loop control variable for all but the innermost array must be references.

Had we neglected the reference and written these loops as:

for (auto row : ia)
	for (auto col : row)

our program would not compile.
As before, the first for iterates through ia, whose elements are arrays of size 4. Because row is not a reference, when the compiler initializes row it will convert each array element (like any other object of array type) to a pointer to that array’s first element.
As a result, in this loop the type of row is int*. The inner for loop is illegal. Despite our intentions, that loop attempts to iterate over an int*.

Pointers and Multidimensional Arrays

int ia[3][4]; // array of size 3; each element is an array of ints of size 4
int (*p)[4] = ia; // p points to an array of four ints
p = &ia[2]; // p now points to the last element in ia
// print the value of each element in ia, with each inner array on its own line
// p points to an array of four ints
for (auto p = ia; p != ia + 3; ++p) {
	// q points to the first element of an array of four ints; that is, q points to an int
	for (auto q = *p; q != *p + 4; ++q)
		cout << *q << ' ';
	
	cout << endl;
}
// p points to the first array in ia
for (auto p = begin(ia); p != end(ia); ++p) {
	// q points to the first element in an inner array
	for (auto q = begin(*p); q != end(*p); ++q)
		cout << *q << ' '; // prints the int value to which q points
	
	cout << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值