3-Strings, Vectors, and Arrays

Please indicate the source: http://blog.csdn.net/gaoxiangnumber1

Welcome to my github: https://github.com/gaoxiangnumber1

3.1. Namespace using Declarations

  • Scope operator(::) says that the compiler should look in the scope of the left-hand operand for the name of the right-hand operand.
  • 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;

A Separate using Declaration Is Required for Each Name

  • Each using declaration introduces a single namespace member.
#include <iostream>

// using declarations for names from the standard library
using std::cin;
using std::cout;
using std::endl;

int main()
{
    cout << "Enter two numbers:" << endl;
    int v1, v2;
    cin >> v1 >> v2;
    cout << "The sum of " << v1 << " and " << v2 << " is " << v1 + v2 << endl;
    return 0;
}

Headers Should Not Include using Declarations

  • Code inside headers(§ 2.6.3, p. 76) should not use using declarations. The reason is that the contents of a header are copied into the including program’s text. If a header has a using declaration, then every program that includes that header gets that same using declaration. As a result, a program that didn’t intend to use the specified library name might encounter unexpected name conflicts.

Exercises Section 3.1

Exercise 3.1

Rewrite the exercises from §1.4.1 (p. 13) and §2.6.2 (p. 76) with appropriate using declarations.

/************************* 1.9 *************************/
#include <iostream>

using std::cout;
using std::endl;

int main()
{
    int num = 50, sum = 0;
    while(num <= 100)
    {
        sum += num;
        ++num;
    }
    cout << "The sum from 50 to 100 is " << sum << endl;

    return 0;
}

/************************* 1.10 *************************/
#include <iostream>

using std::cout;
using std::endl;

int main()
{
    int num = 10;
    while(num >= 0)
    {
        cout << num << endl;
        --num;
    }

    return 0;
}

/************************* 1.11 *************************/
#include <iostream>

using std::cout;
using std::cin;
using std::endl;

int main()
{
    int small = 0, big = 0;
    cout << "Please input 2 integers: ";
    cin >> small >> big;

    if(small > big)
    {
        int temp = small;
        small = big;
        big = small;
    }

    // Print integers in the range [num1, num2]:
    while(small <= big)
    {
        cout << small << endl;
        ++small;
    }

    return 0;
}

/****************************** 1.5.1 ******************************/
#include <iostream>
#include <string>

using std::string;
using std::cin;
using std::cout;

struct Sale_data
{
    string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

int main()
{
    Sale_data book;
    double price;
    cin >> book.bookNo >> book.units_sold >> price;
    book.revenue = book.units_sold * price;
    cout << book.bookNo << " " << book.units_sold << " " << book.revenue << " " << price;

    return 0;
}

/****************************** 1.5.2 ******************************/
#include <iostream>
#include <string>

using std::string;
using std::cin;
using std::cout;
using std::cerr;
using std::endl;

struct Sale_data
{
    string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

int main()
{
    Sale_data book1, book2;
    double price1, price2;
    cin >> book1.bookNo >> book1.units_sold >> price1;
    cin >> book2.bookNo >> book2.units_sold >> price2;
    book1.revenue = book1.units_sold * price1;
    book2.revenue = book2.units_sold * price2;

    if(book1.bookNo == book2.bookNo)
    {
        unsigned totalCnt = book1.units_sold + book2.units_sold;
        double totalRevenue = book1.revenue + book2.revenue;
        cout << book1.bookNo << " " << totalCnt << " " << totalRevenue << " ";
        if (totalCnt != 0)
        {
            cout << totalRevenue / totalCnt << endl;
        }
        else
        {
            cout << "(no sales)" << endl;
        }
        return 0;
    }
    else
    {
        cerr << "Data must refer to same ISBN" << endl;
        return -1; // indicate failure
    }
}

/****************************** 1.6 ******************************/
#include <iostream>
#include <string>

using std::string;
using std::cin;
using std::cout;
using std::cerr;
using std::endl;

struct Sale_data
{
    string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

int main()
{
    Sale_data total;
    double totalPrice;

    if (cin >> total.bookNo >> total.units_sold >> totalPrice)
    {
        total.revenue = total.units_sold * totalPrice;
        Sale_data trans;
        double transPrice;
        while (cin >> trans.bookNo >> trans.units_sold >> transPrice)
        {
            trans.revenue = trans.units_sold * transPrice;
            if (total.bookNo == trans.bookNo)
            {
                total.units_sold += trans.units_sold;
                total.revenue += trans.revenue;
            }
            else
            {
                cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " ";
                if (total.units_sold != 0)
                {
                    cout << total.revenue / total.units_sold << endl;
                }
                else
                {
                    cout << "(no sales)" << endl;
                }
                total.bookNo = trans.bookNo;
                total.units_sold = trans.units_sold;
                total.revenue = trans.revenue;
            }
        }
        cout << total.bookNo << " " << total.units_sold << " " << total.revenue << " ";
        if (total.units_sold != 0)
        {
            cout << total.revenue / total.units_sold << endl;
        }
        else
        {
            cout << "(no sales)" << endl;
        }
        return 0;
    }
    else
    {
        cerr << "No data?!" << endl;
        return -1;  // indicate failure
    }
}

3.2. Library string Type

  • A string is a variable-length sequence of characters and it is defined in the string header.

3.2.1. Defining and Initializing strings

  • Each class defines how objects of its type can be initialized. Table 3.1 lists the most common ways to initialize strings.

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. 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, we must use the direct form of initialization.
string s1 = "hiya"; // copy initialization
string s2("hiya"); // direct initialization
string s3(10, 'c'); // direct initialization
  • When we want to use several values, we can indirectly use the copy form of initialization by explicitly creating a (temporary) object to copy.
    string s4 = string(10, 'c'); // copy initialization; s4 is cccccccccc

3.2.2. Operations on strings

Reading and Writing strings

  • The string operators return their left-hand operand as their result. Thus, we can chain together multiple reads or writes.

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;
}
  • Once we hit end-of-file or invalid input, we fall out of the while.

Using getline to Read an Entire Line

  • The getline function takes an input stream and a string, it 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 is a newline, then the resulting string is the empty string.
  • getline returns its istream argument and we can use getline as a condition.
#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::string;

int main()
{
    string line;
    while(getline(cin, line))
    {
        cout << line << '\n';
    }
    return 0;
}

The string empty and size Operations

  • empty() returns a bool indicating whether the string is empty and it is a member function of string.
// read input a line at a time and discard blank lines
while (getline(cin, line))
    if (!line.empty())
        cout << line << endl;
  • size() returns the length of a string(i.e., the number of characters in it) which is of string::size_type value. Any variable used to store the result from the string size operation should be of type string::size_type.
  • The string class defines several companion types that make it possible to use the library types in a machine-independent manner. The type size_type is one of these companion types.
  • string::size_type is an unsigned type, expressions that mix signed and unsigned data can have surprising results(§ 2.1.2, p. 36). If n is an int that holds a negative value, then s.size() < n will almost surely evaluate as true because n will convert to a large unsigned value.
#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::string;

int main()
{
    string str = "gaoxiangnumber1";
    if(str.size() < -1)
    {
        cout << "int -> unsigned";
    }

    return 0;
}
/*
Output:
int -> unsigned
*/
  • Avoid problems due to conversion between unsigned and int by not using ints in expressions that use size().

Comparing strings

  • The equality operators(== and !=) test whether two strings are equal or unequal, respectively. Two strings are equal if they are the same length and contain the same characters.
  • 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 a case-sensitive dictionary strategy:
    1. 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.
    2. 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.
"Hello" < "Hello World";
"Hiya" > "Hello"
"Hiya" > "Hello World"

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

  • When we use the plus operator (+) on strings, the result is a new string whose characters are a copy of those in the left-hand operand followed by those from the right-hand operand. The compound assignment operator(+=) appends the right-hand operand to the left-hand string.
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 string s

  • The string library lets us convert both character literals and character string literals to strings.
  • When we mix strings and string or character literals, at least one operand to each + operator must be of string type:
string str1 = "gao";
string str2 = str1 + 'x';  // str2 = "gaox"
string str3 = str2 + "iang";  // str3 = "gaoxiang"
// error: invalid operands of types ‘const char [4]’ and ‘const char [6]’ to binary ‘operator+’
string str4 = "gao" + "xiang";  // Error
string str5 = str3 + "number" + "one";  // str5 = "gaoxiangnumberone"
//error: invalid operands of types ‘const char [7]’ and ‘const char [8]’ to binary ‘operator+’
string str6 = "Hello " + "World, " + str5;  // Error
  • The initialization of str5 groups as
    string str5 = (str3 + "number") + "one";
  • The subexpression str3 + “number” returns a string, which forms the left-hand operand of the second + operator. It is as if we had written
string tmp = str3 + "number"; // ok: + has a string operand
str5 = tmp + "one"; // ok: + has a string operand
  • The initialization of str6 is illegal:
    string str6 = ("Hello " + "World, ") + str5;

Exercises Section 3.2.2

Exercise 3.2

Write a program to read the standard input a line at a time. Modify your program to read a word at a time.

#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::endl;
using std::string;

int main()
{
    // One line at a time:
    string str;
    while(getline(cin, str))
    {
        cout << str << endl;
    }

    return 0;
}
#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::endl;
using std::string;

int main()
{
    // One line at a time:
    string str;
    while(cin >> str)
    {
        cout << str << endl;
    }

    return 0;
}

Exercise 3.3

Explain how whitespace characters are handled in the string input operator and in the getline function.

  • string input operator: all whitespaces are ignored.
  • getline: keep whitespaces in the same line as a string.

Exercise 3.4

Write a program to read two strings and report whether the strings are equal. If not, report which of the two is larger. Now, change the program to report whether the strings have the same length, and if not, report which is longer.

#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::endl;
using std::string;

int main()
{
    string str1, str2;
    cin >> str1 >> str2;
    if(str1 == str2)
    {
        cout << "Equal\n";
    }
    else
    {
        cout << "Not equal: " << ((str1 > str2) ? str1 : str2) << " is larger.\n";
    }

    return 0;
}
#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::endl;
using std::string;

int main()
{
    string str1, str2;
    cin >> str1 >> str2;
    string::size_type length1 = str1.size(), length2 = str2.size();
    if(length1 == length2)
    {
        cout << "Same length\n";
    }
    else
    {
        cout << ((length1 > length2) ? str1 : str2) << " is longer.\n";
    }

    return 0;
}

Exercise 3.5

Write a program to read strings from the standard input, concatenating what is read into one large string. Print the concatenated string. Next, change the program to separate adjacent input strings by a space.

#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::endl;
using std::string;

int main()
{
    string input, total;
    while(cin >> input)
    {
        total += input;
    }
    cout << total;

    return 0;
}
#include <iostream>
#include <string>

using std::cout;
using std::cin;
using std::endl;
using std::string;

int main()
{
    string input, total;
    while(cin >> input)
    {
        total = total + ' ' + input;
    }
    cout << total;

    return 0;
}

3.2.3. Dealing with the Characters in a string

Advice: Use the C++ Versions of C Library Headers

  • Headers in C have names of the form “name.h”. The C++ versions of these headers remove the .h suffix and precede the name with the letter c. The names defined in the headers are defined inside the std namespace, whereas those defined in the
for (declaration : expression)
    statement
  • expression is an object of a type that represents a sequence, and declaration defines the variable that we’ll use to access the elements in the sequence.
  • On each iteration, the variable in declaration is initialized from the value of the next element in expression.
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;

Using a Range for to Change the Characters in a string

  • If we want to change the value of the characters in a string, we must define the loop variable as a reference type(§ 2.3.1, p. 50).
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?

  • Two ways to access individual characters in a string: subscript/iterator.
  • The subscript operator([]) takes a string::size_type value that denotes the position of the character we want to access. The operator returns a reference to the character at the given position.
  • Subscripts for strings start at zero and the last character is in s[s.size() - 1]. The result of using an index outside this range is undefined. Subscripting an empty string is undefined.
  • If our index has a signed type, its value will be converted to the unsigned type that string::size_type represents(§ 2.1.2, p. 36).

Using a Subscript for Iteration

  • AND operator(&&) yields true if both operands are true and false otherwise. It evaluates its right-hand operand only if the left-hand operand is true.

Caution: Subscripts are Unchecked

  • When we use a subscript, we must ensure that the subscript is in range [0, size() - 1].

Using a Subscript for Random Access

Exercises Section 3.2.3

Exercise 3.6

Use a range for to change all the characters in a string to X.

#include <iostream>
#include <string>

using std::cout;
using std::string;

int main()
{
    string str = "gaoxiangnumber1";
    for(char &ch : str)
    {
        ch = 'X';
    }
    cout << str;

    return 0;
}
/*
Output:
XXXXXXXXXXXXXXX
*/

Exercise 3.7

What would happen if you define the loop control variable in the previous exercise as type char? Predict the results and then change your program to use a char to see if you were right.

#include <iostream>
#include <string>

using std::cout;
using std::string;

int main()
{
    string str = "gaoxiangnumber1";
    for(char ch : str)
    {
        ch = 'X';
    }
    cout << str;

    return 0;
}
/*
Output:
gaoxiangnumber1
*/

Exercise 3.8

Rewrite the program in the first exercise, first using a while and again using a traditional for loop. Which of the three approaches do you prefer and why?

#include <iostream>
#include <string>

using std::cout;
using std::string;

int main()
{
    string str1 = "gaoxiangnumber1";
    string str2 = str1;
    int length = str1.size();

    // First: use while
    int index1 = 0;
    while(index1 < length)
    {
        str1[index1] = 'X';
        ++index1;
    }

    // Second, use traditional for
    for(int index2 = 0; index2 < length; ++index2)
    {
        str2[index2] = 'X';
    }
    cout << str1 << '\t' << str2;

    return 0;
}
/*
Output:
XXXXXXXXXXXXXXX XXXXXXXXXXXXXXX
*/

Exercise 3.9

What does the following program do? Is it valid? If not, why not?

string s;
cout << s[0] << endl;

Invalid: undefined behavior.

Exercise 3.10

Write a program that reads a string of characters including punctuation and writes what was read but with the punctuation removed.

#include <iostream>
#include <string>
#include <cctype>  // ispunct(c)

using std::cout;
using std::cin;
using std::string;

int main()
{
    string str;
    cin >> str;
    int length = str.size();
    for(int index = 0; index < length; ++index)
    {
        if(ispunct(str[index]))
        {
            continue;
        }
        cout << str[index];
    }

    return 0;
}

Exercise 3.11

Is the following range for legal? If so, what is the type of c?

const string s = "Keep out!";
for (auto &c : s) { /* ... */ }
  • When c = ‘X’; error: assignment of read-only reference ‘c’.
    So, c is type of “const char &”.

3.3. Library vector Type

  • A vector is a collection of one type objects. Every object in the collection has an associated index, which gives access to that object.
#include <vector>
using std::vector;
  • A vector is a class template, not a type. . Templates are not themselves functions or classes, 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. When we use a template, we specify what kind of class or function we want the compiler to instantiate.
  • For a class template, we specify which class to instantiate by supplying additional information inside a pair of angle brackets following the template’s name.
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
  • Because references are not objects(§ 2.3.1, p. 50), we cannot have a vector of references.
  • C++11: vector

3.3.1. Defining and Initializing vectors

List Initializing a vector

  • C++11: List initialize(§ 2.2.1, p. 43) a vector from a list of zero or more initial element values enclosed in curly braces:
    `vector articles = {“a”, “an”, “the”};
  • C++ provides several forms of initialization(§ 2.2.1, p. 43). Restrictions:
    1. When we use the copy initialization form(use =) (§ 3.2.1, p. 84), we can supply only a single initializer;
    2. When we supply an in-class initializer(§ 2.6.1, p. 73), we must use copy initialization or curly braces.
    3. We can supply a list of element values only by using list initialization in which the initializers are enclosed in curly braces. We cannot supply a list of initializers using parentheses.
vector<int> vec1 {1, 2, 3, 4, 5};
// error: no matching function for call to ‘std::vector<int>::vector(int, int, int, int, int)’
vector<int> vec2 (1, 2, 3, 4, 5);

Value Initialization

  • When we omit the value and supply only a size, the library creates a value-initialized element initializer for us whose value depends on the type of the elements stored in the vector and is used to initialize each element in the container.
  • If the vector holds built-in type elements, then the element initializer has a value of 0. If the elements are of a class type(string…), then the element initializer is itself default initialized.
vector<int> ivec(10); // ten elements, each initialized to 0
vector<string> svec(10); // ten elements, each an empty string
  • Two restrictions on this form of initialization:
    1. Some classes require that we always supply an explicit initializer(§ 2.2.1, p. 44). If our vector holds these types’ objects, we must supply an initial element value.
    2. When we supply only an element count, we must use the direct form of initialization:
      vector<int> vi = 10; // error: must use direct initialization to supply a size

List Initializer or Element Count?

  • In a few cases, what initialization means depends upon whether we use curly braces or parentheses to pass the initializer(s).
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
  • When we use curly braces, we’re saying 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 there is no way to use the initializers to list initialize the object, 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"

Exercises Section 3.3.1

Exercise 3.12

Which, if any, of the following vector definitions are in error? For those that are legal, explain what the definition does. For those that are not legal, explain why they are illegal.
(a) vector

Exercise 3.13

How many elements are there in each of the following vectors? What are the values of the elements?
(a) vector v1; empty
(b) vector v2(10); ten zero
(c) vector v3(10, 42); ten 42
(d) vector v4{10}; 10
(e) vector v5{10, 42}; 10, 42
(f) vector v6{10}; 10 “”
(g) vector v7{10, “hi”};

  • size:0, empty vector.
  • size:10, ten zero.
  • size:10, ten 42.
  • size:1, one 10.
  • size:2, 10 and 42.
  • size:10, ten empty string “”
  • size:10, ten string “hi”

3.3.2. Adding Elements to a vector

  • The push_back operation takes a value and “pushes” that value as a new last element onto the “back” of the 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

Key Concept: vectors Grow Efficiently

  • Because vectors grow efficiently, it is unnecessary to define a vector of a specific size. The exception is if all the elements actually need the same value.

Implications of Adding Elements to a vector

  • We must ensure that any loops we write are correct even if the loop changes the size of the vector.
  • § 5.4.3 (p. 188): we cannot use a range for if the body of the loop adds elements to the vector.

Exercises Section 3.3.2

Exercise 3.14

Write a program to read a sequence of ints from cin and store those values in a vector.

#include <iostream>
#include <vector>

using std::cout;
using std::cin;
using std::vector;

void Print(vector<int> &vec)
{
    int length = vec.size();
    for(int index = 0; index < length; ++index)
    {
        cout << vec[index] << " ";
    }
    cout << '\n';
}

int main()
{
    vector<int> vec;
    for(int num = 0; cin >> num; )
    {
        vec.push_back(num);
    }
    Print(vec);

    return 0;
}

Exercise 3.15

Repeat the previous program but read strings this time.

#include <iostream>
#include <string>
#include <vector>

using std::cout;
using std::cin;
using std::string;
using std::vector;

void Print(vector<string> &vec)
{
    int length = vec.size();
    for(int index = 0; index < length; ++index)
    {
        cout << vec[index] << " ";
    }
    cout << '\n';
}

int main()
{
    vector<string> vec;
    for(string str; cin >> str; )
    {
        vec.push_back(str);
    }
    Print(vec);

    return 0;
}

3.3.3. Other vector Operations

  • The size member returns a value of the size_type defined by the corresponding vector type. We must name the type in which it is defined:
vector<int>::size_type  // ok
vector::size_type           // error
  • We can compare two vectors only if we can compare the elements in those vectors. The relational operators apply a dictionary ordering:
    1. If the vectors have differing sizes, but the elements that are in common are equal, then the vector with fewer elements is less than the one with more elements.
    2. If the elements have differing values, then the relationship between the vectors is determined by the relationship between the first elements that differ.

Computing a vector Index

  • Subscripts for vector start at 0; the type of a subscript is the corresponding size_type.

Subscripting Does Not Add Elements

  • The subscript operator on vector(and string) fetches an existing element; it does not add an element.
    E.g.: ivec is an empty vector and there are no elements to subscript.
vector<int> ivec;       // empty vector
for (decltype(ivec.size()) ix = 0; ix != 10; ++ix)
    ivec[ix] = ix;      // Segmentation fault

Caution: Subscript Only Elements that are Known to Exist!

vector<int> ivec;       // empty vector
cout << ivec[0];        // Segmentation fault! error: ivec has no elements!
vector<int> ivec2(10);  // vector with ten elements
cout << ivec2[10];  // Segmentation fault! error: ivec2 has elements 0 . . . 9

Exercises Section 3.3.3

Exercise 3.16

Write a program to print the size and contents of the vectors from exercise 3.13. Check whether your answers to that exercise were correct. If not, restudy § 3.3.1 (p. 97) until you understand why you were wrong.

#include <iostream>
#include <string>
#include <vector>

using std::cout;
using std::cin;
using std::string;
using std::vector;

template<typename T>
void Print(vector<T> &vec)
{
    int length = vec.size();
    for(int index = 0; index < length; ++index)
    {
        cout << vec[index] << " ";
    }
    cout << ((length == 0) ? "empty\n" : "\n");
}

int main()
{
    vector<int> v1;
    vector<int> v2(10);
    vector<int> v3(10, 42);
    vector<int> v4 {10};
    vector<int> v5 {10, 42};
    vector<string> v6 {10};
    vector<string> v7 {10, "hi"};
    cout << "v1:\t";
    Print(v1);
    cout << "v2:\t";
    Print(v2);
    cout << "v3:\t";
    Print(v3);
    cout << "v4:\t";
    Print(v4);
    cout << "v5:\t";
    Print(v5);
    cout << "v6:\t";
    Print(v6);
    cout << "v7:\t";
    Print(v7);

    return 0;
}

Exercise 3.17

Read a sequence of words from cin and store the values a vector. After you’ve read all the words, process the vector and change each word to uppercase. Print the transformed elements, eight words to a line.

#include <iostream>
#include <string>
#include <vector>

using std::cout;
using std::cin;
using std::string;
using std::vector;

template<typename T>
void Print(vector<T> &vec)
{
    int length = vec.size();
    int cnt = 0;
    for(int index = 0; index < length; ++index)
    {
        cout << vec[index] << " ";
        ++cnt;
        if(cnt % 8 == 0)
        {
            cout << '\n';
        }
    }
    cout << '\n';
}

int main()
{
    vector<string> vec;
    for(string str1; cin >> str1; )
    {
        vec.push_back(str1);
    }
    int length1 = vec.size();
    for(int index1 = 0; index1 < length1; ++index1)
    {
        string &str2 = vec[index1];
        int length2 = str2.size();
        for(int index2 = 0; index2 < length2; ++index2)
        {
            char &ch = str2[index2];
            ch = (('a' <= ch && ch <= 'z') ? ch - 32 : ch);
        }
    }
    Print(vec);

    return 0;
}

Exercise 3.18

Is the following program legal? If not, how might you fix it?

vector<int> ivec;
ivec[0] = 42;

ivec.push_back(42);

Exercise 3.19

List three ways to define a vector and give it ten elements, each with the value 42. Indicate whether there is a preferred way to do so and why.

vector<int> vec1(10, 42);
vector<int> vec2 {42, 42, 42, 42, 42, 42, 42, 42, 42, 42};
vector<int> vec3;
for (int index = 0; index != 10; ++index)
{
    vec3.push_back(42);
}

Exercise 3.20

Read a set of integers into a vector. Print the sum of each pair of adjacent elements. Change your program so that it prints the sum of the first and last elements, followed by the sum of the second and second-to-last, and so on.

#include <iostream>
#include <vector>

using std::cout;
using std::cin;
using std::vector;

int main()
{
    vector<int> vec;
    int input = 0, length = 1;
    cin >> input;
    vec.push_back(input);

    // First print the sum of each pair of adjacent elements:
    cout << "Sum of each pair of adjacent elements:\n";
    int first = input;
    while(cin >> input)
    {
        cout << first + input << ' ';
        first = input;
        vec.push_back(input);
        ++length;
    }

    // Second print the sum of the first and last elements, and so on.
    cout << "\nSum of the first and last elements, and so on:\n";
    for(int index = 0; index < length / 2; ++index)
    {
        cout << vec[index] + vec[length - 1 - index] << ' ';
    }

    return 0;
}

3.4. Introducing Iterators

  • Iterators give us indirect access to an object. 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.

3.4.1. Using Iterators

  • Types that have iterators have members that return iterators.
  • The begin member returns an iterator that denotes the first element, if there is one.
  • The iterator returned by end is an iterator positioned “one past the end” of the container or string. This iterator denotes a nonexistent element “off the end” of the container. It is used as a marker indicating when we have processed all the elements.
  • If the container is empty, the iterators returned by begin and end are equal—they are both off-the-end iterators.
// 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

Iterator Operations

  • We can dereference only a valid iterator that denotes an element, dereferencing an invalid iterator or an off-the-end iterator has undefined behavior.
  • 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.
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
}
  • Iterators use the increment(++) operator to move from one element to the next.
  • Because the iterator returned from end does not denote an element, it may not be incremented or dereferenced.
// process characters in s until we run out of characters or we hit a whitespace
for (auto it = s.begin(); it != s.end() && !isspace(*it); ++it)
    *it = toupper(*it);  // capitalize the current character

Iterator Types

vector<int> vec1(10, 618);
vector<int>::iterator it1 = vec1.begin();
*it1 = 6180;
vector<int>::const_iterator it2 = vec1.begin();
// error: assignment of read-only location ‘it2.operator*<const int*, std::vector<int>>()’
*it2 = 61800;

const vector<int> vec2(10, 71);
// error: conversion from ‘std::vector<int>::const_iterator ’ to ‘std::vector<int>::iterator’
vector<int>::iterator it3 = vec2.begin();
vector<int>::const_iterator it4 = vec2.begin();
// error: assignment of read-only location ‘it4.operator*<const int*, std::vector<int>>()’
*it4 = 618;
  • If a container is const, we may use only its const_iterator type. With a nonconst container, we can use either iterator or const_iterator.
  • A const_iterator may read but not write the element it denotes; an object of type iterator can both read and write.

The begin and end Operations

  • The type returned by begin and end depends on whether the object on which they operator is const. 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
  • C++11: Regardless of whether the container is const, cbegin and cend return a const_iterator.
    auto it3 = v.cbegin(); // it3 has type vector<int>::const_iterator

Combining Dereference and Member Access

vector<string> vec(10, "gaoxiang");
vector<string>::iterator it = vec.begin();
if((*it).empty());  // ok
// error: ‘std::vector<std::basic_string<char> >::iterator’ has no member named ‘empty’
if(*it.empty());
  • The parentheses in (*it).empty() are necessary. The parentheses say to apply the dereference operator to it and to apply the dot operator to the result of dereferencing it. Without parentheses, *it.empty() is interpreted as a request to fetch the empty member from the object named it.
  • The arrow operator(->) combines dereference and member access into a single operation: it->mem = (*it).mem.

Some vector Operations Invalidate Iterators

  • Any operation that changes the size of a vector potentially invalidates all iterators into that vector. We cannot add elements to a vector inside a range for loop.

Exercises Section 3.4.1

Exercise 3.21

Redo the first exercise from §3.3.3(p.105) using iterators.

#include <iostream>
#include <string>
#include <vector>

using std::cout;
using std::cin;
using std::string;
using std::vector;

template<typename T>
void Print(vector<T> &vec)
{
    typename vector<T>::const_iterator it1 = vec.cbegin(), it2 = vec.cend();
    for(; it1 != it2; ++it1)
    {
        cout << *it1 << " ";
    }
    cout << ((vec.cbegin() == vec.cend()) ? "empty\n" : "\n");
}

int main()
{
    vector<int> v1;
    vector<int> v2(10);
    vector<int> v3(10, 42);
    vector<int> v4 {10};
    vector<int> v5 {10, 42};
    vector<string> v6 {10};
    vector<string> v7 {10, "hi"};
    cout << "v1:\t";
    Print(v1);
    cout << "v2:\t";
    Print(v2);
    cout << "v3:\t";
    Print(v3);
    cout << "v4:\t";
    Print(v4);
    cout << "v5:\t";
    Print(v5);
    cout << "v6:\t";
    Print(v6);
    cout << "v7:\t";
    Print(v7);

    return 0;
}

Exercise 3.22

Revise the loop that printed the first paragraph in text to instead change the elements in text that correspond to the first paragraph to all uppercase. After you’ve updated text, print its contents.

#include <iostream>
#include <vector>
#include <string>

using std::cout;
using std::cin;
using std::endl;
using std::vector;
using std::string;

int main()
{
    vector<string> text;
    for(string line; getline(cin, line); )
    {
        text.push_back(line);
    }

    vector<string>::iterator it = text.begin();
    for(; it != text.end() && !it->empty(); ++it)
    {
        for(char &ch : *it)
        {
            ch = (('a' <= ch && ch <= 'z') ? ch -32 : ch);
        }
        cout << *it << endl;
    }

    return 0;
}

Exercise 3.23

Write a program to create a vector with ten int elements. Using an iterator, assign each element a value that is twice its current value. Test your program by printing the vector.

#include <iostream>
#include <vector>

using std::cin;
using std::cout;
using std::vector;

template<typename T>
void Double(vector<T> &vec)
{
    typename vector<T>::iterator it1 = vec.begin(), it2 = vec.end();
    for(; it1 != it2; ++it1)
    {
        *it1 *= 2;
    }
}

template<typename T>
void Print(vector<T> &vec)
{
    typename vector<T>::const_iterator it1 = vec.cbegin(), it2 = vec.cend();
    for(; it1 != it2; ++it1)
    {
        cout << *it1 << " ";
    }
    cout << ((vec.cbegin() == vec.cend()) ? "empty\n" : "\n");
}

int main()
{
    vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    Double(vec);
    Print(vec);

    return 0;
}

3.4.2. Iterator Arithmetic

  • All the library containers have iterators that support increment. We can use == and != to compare two valid iterators(§ 3.4, p. 106) into any of the library container types.
  • Iterators for string and vector support additional operations:

Arithmetic Operations on Iterators

  • When we add or subtract an integral value and an iterator, the result must denote an element in the same vector (or string) or denote one past the end of the associated vector (or string)
  • We can compare vector and string iterators using the relational operators(<, <=, >, >=). The iterators must be valid and must denote elements in(or one past the end of) the same vector or string.
  • We can subtract two iterators if they refer to elements in, or one off the end of, the same vector or string. The result is the distance(the amount by which we’d have to change one iterator to get the other) between the iterators. The result type is a signed integral type named difference_type.

Using Iterator Arithmetic

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

Exercises Section 3.4.2

Exercise 3.24

Redo the last exercise from § 3.3.3 (p. 105) using iterators.

#include <iostream>
#include <vector>

using std::cout;
using std::cin;
using std::vector;

int main()
{
    vector<int> vec;
    int input = 0, length = 1;
    cin >> input;
    vec.push_back(input);

    // First print the sum of each pair of adjacent elements:
    cout << "Sum of each pair of adjacent elements:\n";
    int first = input;
    while(cin >> input)
    {
        cout << first + input << ' ';
        first = input;
        vec.push_back(input);
        ++length;
    }

    // Second print the sum of the first and last elements, and so on.
    cout << "\nSum of the first and last elements, and so on:\n";
    vector<int>::const_iterator it1 = vec.cbegin(), it2 = it1 + length / 2, it3 = vec.cend() - 1;
    for( ; it1 != it2; ++it1, --it3)
    {
        cout << *it1 + *it3 << ' ';
    }

    return 0;
}

Exercise 3.25

Rewrite the grade clustering program from § 3.3.3 (p. 104) using iterators instead of subscripts.

#include <iostream>
#include <vector>

using std::cout;
using std::cin;
using std::vector;

int main()
{
    vector<int> scores(11, 0);
    int grade;
    while (cin >> grade)
    {
        if(grade <= 100)
        {
            ++(*(scores.begin() + grade / 10));
        }
    }

    for(int score : scores)
    {
        cout << score << " ";
    }
    cout << '\n';

    return 0;
}

Exercise 3.26

In the binary search program on page 112, why did we write mid = beg + (end - beg) / 2; instead of mid = (beg + end) / 2?

  • Because the iterator of vector don’t define the + operator between the two iterators. So, beg + end is illegal. We can only use the subtraction between the two iterators.

3.5. Arrays

  • An array is a container of a single type objects that we access by position. Arrays have fixed size; we cannot add elements to an array.

3.5.1. Defining and Initializing Built-in Arrays

  • Arrays are a compound type(§ 2.3, p. 50). An array declarator has the form a[d], where a is the name being defined and d is the dimension of the array that must be greater than zero. The number of elements in an array is part of the array’s type. The dimension must be a constant expression(§ 2.4.4, p. 65) and known at compile time.
unsigned cnt = 42; // not a constant expression
constexpr unsigned sz = 42; // constant expression
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
int num1, num2 = 10;
cin >> num1;
int arr1[num1], arr2[num2];  // ok
  • By default, the elements in an array are default initialized(§ 2.2.1, p. 43). A default-initialized array of built-in type that is defined inside a function will have undefined values.
  • When we define an array, we must specify a type for the array. We cannot use auto to deduce the type from a list of initializers. Arrays hold objects, so there are no arrays of references.

Explicitly Initializing Array Elements

  • When we list initialize(§ 3.3.1, p. 98) the elements in an array, we can omit the dimension and 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(§3.3.1, p. 98):
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

Character Arrays Are Special

  • We can initialize character arrays from a string literal(§ 2.1.3, p. 39). Remember string literals end with a null character and the null character is copied into the array along with the characters in the literal.
char a1[] = {'C', '+', '+'};        // list initialization, no null. char a1[3]
char a2[] = {'C', '+', '+', '\0'};  // list initialization, explicit null. char a2[4]
char a3[] = "C++";          // null terminator added automatically. char a3[4]
// error: initializer-string for array of chars is too long
const char a4[6] = "Daniel";    // At least char[7]

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:
int a[] = {0, 1, 2};
int a2[] = a;   // error: cannot initialize one array with another
a2 = a;     // error: cannot assign one array to another

Understanding Complicated Array Declarations

int *ptrs[10];      // ptrs is an array of ten pointers to int
int &refs[10];  // error: declaration of ‘refs’ as array 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

Exercises Section 3.5.1

Exercise 3.27

Assuming txt_size is a function that takes no arguments and returns an int value, which of the following definitions are illegal? Explain why.
unsigned buf_size = 1024;
(a) int ia[buf_size]; ok
(b) int ia[4 * 7 - 14]; ok
(c) int ia[txt_size()]; ok
(d) char st[11] = “fundamental”; error

  • Legal.
  • Legal.
  • Legal.
  • Illegal: char st[12] or larger.

Exercise 3.28

What are the values in the following arrays?

string sa[10];
int ia[10];
int main()
{
    string sa2[10];
    int ia2[10];
}
  • Empty string:””
  • 0
  • Empty string: “”
  • Undefined.

Exercise 3.29

List some of the drawbacks of using an array instead of a vector.

  • Can’t grow.

3.5.2. Accessing the Elements of an Array

  • We can use a range for or the subscript(range: [0, array.size - 1]) operator to access elements of an array.
  • Array’s subscripts’ type is size_t which is an unsigned type that is guaranteed to be large enough to hold the size of any object in memory and it is defined in the .

Exercises Section 3.5.2

Exercise 3.30

Identify the indexing errors in the following code:

constexpr size_t array_size = 10;
int ia[array_size];
for (size_t ix = 1; ix <= array_size; ++ix)
    ia[ix] = ix;
  • [0, 9]

Exercise 3.31

Write a program to define an array of ten ints. Give each element the same value as its position in the array.

int arr1[10]
for(int index = 0; index < 10; ++index)
    arr1[index] = index;

Exercise 3.32

Copy the array you defined in the previous exercise into another array. Rewrite your program to use vectors.

int arr2[10];
for(int index = 0; index < 10; ++index)
    arr2[index] = arr1[index];

Exercise 3.33

What would happen if we did not initialize the scores array in the program on page 116?

  • The results will be undefined!

3.5.3. Pointers and Arrays

  • We can obtain a pointer to an array element by taking the address of that element. In most places when we use an array, the compiler automatically substitutes a pointer to the first element:
string nums[] = {"one", "two", "three"};
string *p = &nums[0];   // p points to the first element in nums
string *p2 = nums;      // equivalent to p2 = &nums[0], so: p = p2
  • When we use an array as an initializer for a variable defined using auto(§ 2.5.2, p. 68), 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
  • This conversion does not happen when we use decltype(§ 2.5.3, p. 70): 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};
decltype(ia) ia4 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0};//error: too many initializers for ‘int [10]’
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

Pointers Are Iterators

  • Pointers to array elements support the same operations as iterators on vectors or strings(§ 3.4, p. 106).
int arr[] = {0,1,2,3,4,5,6,7,8,9};
int *p = arr; // p points to the first element in arr
++p; // p points to arr[1]
  • We can take the address of the nonexistent element one past the last element of an array.
    int *e = &arr[10]; // pointer just past the last element in arr
    Like an off-the-end iterator(§ 3.4.1, p. 106), an off-the-end pointer does not point to an element. The only thing we can do with this element is take its address, we may not dereference or increment an off-the-end pointer.

The Library begin and end Functions

  • C++11: begin and end take an argument that is an array and they are defined in .
int ia[] = {0,1,2,3,4,5,6,7,8,9};
int *beg = std::begin(ia);  // pointer to the first element in ia
int *last = std::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.
int *pbeg = begin(arr), *pend = end(arr);
// find the first negative element, stopping if we've seen all the elements
while (pbeg != pend && *pbeg >= 0)
    ++pbeg;

Pointer Arithmetic

  • Pointers that address array elements can use all the iterator operations listed in Table 3.6(p. 107) and Table 3.7(p. 111).
  • When we add(or subtract) an integral value to(or from) a pointer, the result is a new pointer that points to the element the given number ahead of(or behind) the original pointer. The result of adding an integral value to a pointer must be a pointer to an element in the same array, or a pointer just past the end of the array.
// ok: arr is converted to a pointer to its first element; p points one past the end of arr
int *p = arr + sz;      // use caution -- do not dereference!
int *p2 = arr + 10; // error: arr has only 5 elements; p2 has undefined value
  • Subtracting two pointers gives us the distance between those pointers. The pointers must point to elements in the same array.
    auto n = end(arr) - begin(arr); // n is 5, the number of elements in arr
  • The result of subtracting two pointers is ptrdiff_t type that is a machine-specific type and is defined in . Because subtraction might yield a negative distance, ptrdiff_t is a signed integral type.
  • We can use the relational operators to compare pointers that point to elements of an array, or one past the last element in that array. We cannot use the relational operators on pointers to two unrelated objects.
#include <iostream>

using std::cout;
using std::begin;
using std::end;

int main()
{
    int arr1[] = {1, 2, 3, 4, 5};
    int arr2[] = {7, 8, 9, 0, 10, 55};
    int *ptr1 = begin(arr1), *ptr2 = end(arr1), *ptr3 = end(arr2);
    while(ptr1 < ptr2)
    {
        cout << *ptr1 << ' ';
        ++ptr1;
    }
    cout << '\n';
    ptr1 = begin(arr1);
    while(ptr1 < ptr3)
    {
        cout << *ptr1 << ' ';
        ++ptr1;
    }
    return 0;
}
/*
Output:
1 2 3 4 5 
1 2 3 4 5 32767 0 0 7 8 9 0 10 55 
*/
  • Pointer arithmetic is valid for null pointers(§ 2.3.2, p. 53) and for pointers that point to an object that is not an array.
    1. If p is a null pointer, we can add or subtract an integral constant expression(§ 2.4.4, p. 65) whose value is 0 to p. We can also subtract two null pointers from one another, in which case the result is 0.
    2. In the latter case, the pointers must point to the same object, or one past that object.

Interaction between Dereference and Pointer Arithmetic

  • The result of adding an integral value to a pointer is itself a pointer. Assuming the resulting pointer points to an element, we can dereference the resulting pointer:
int ia[] = {0, 2, 4, 6, 8}; // array with 5 elements of type int
int last = *(ia + 4);       // ok: initializes last to 8, the value of ia[4]
  • The parentheses around this pointer addition are essential.
    last = *ia + 4; // last = 4, equivalent to ia[0] + 4
    means dereference ia and add 4 to the dereferenced value.

Subscripts and Pointers

  • In most places when we use the name of an array, we are really using a pointer to the first element in that array.
  • When we subscript an array, we are really subscripting a pointer to an element in that array. We can use the subscript operator on any pointer, as long as that pointer points to an element(or one past the last element) in an array.
int arr[] = {1, 2, 3, 4, 5, 6, 7};
int num1 = arr[2];  // // arr is converted to a pointer to the first element in arr
int *ptr1 = arr;  // ptr1 points to the first element in arr
int num2 = *(ptr1 + 2);  // equivalent to num2 = arr[2]
int *ptr3 = &arr[3];  // ptr3 points to the element indexed by 3
int num3 = ptr3[1];  // equivalent to *(ptr3 + 1), the same element as arr[4]
int num4 = ptr3[-3];  // the same element as arr[0]
cout << num2 << ' ' << num3 << ' ' << num4;  // 3 5 1
  • The library types(vector, string, etc) force the index used with a subscript to be an unsigned value. But the index of the built-in subscript operator is not an unsigned type, can be a negative value.

Exercises Section 3.5.3

Exercise 3.34

Given that p1 and p2 point to elements in the same array, what does the following code do? Are there values of p1 or p2 that make this code illegal?
p1 += p2 - p1;
- Make p1 = p2. Always legal.

Exercise 3.35

Using pointers, write a program to set the elements in an array to zero.

int arr[10];
for(int *ptr1 = std::begin(arr), *ptr2 = std::end(arr); ptr1 != ptr2; ++ptr1)
{
    *ptr1 = 0;
}

Exercise 3.36

Write a program to compare two arrays for equality. Write a similar program to compare two vectors.

#include <iostream>
#include <vector>

using std::cout;
using std::begin;
using std::end;
using std::vector;

bool CompareArray(int *arr1_begin, int *arr1_end, int *arr2_begin, int *arr2_end)
{
    int length1 = arr1_end - arr1_begin, length2 = arr2_end - arr2_begin;
    if(length1 != length2)
    {
        return false;
    }
    for(int index = 0; index < length1; ++index)
    {
        if(*(arr1_begin + index) != *(arr2_begin + index))
        {
            return false;
        }
    }
    return true;
}

bool CompareVector(vector<int> &vec1, vector<int> &vec2)
{
    if(vec1 == vec2)
    {
        return true;
    }
    return false;
}

int main()
{
    int arr1[] = {1, 2, 3, 4, 5}, arr2[] = {1, 2, 3, 4, 5};
    cout << CompareArray(begin(arr1), end(arr1), begin(arr2), end(arr2)) << '\n';
    vector<int> vec1(5, 6), vec2(5, 7);
    cout << CompareVector(vec1, vec2);

    return 0;
}
/*
Output:
1
0
*/

3.5.4. C-Style Character Strings

  • C-style strings are stored in character arrays and are null terminated that the last character in the string is followed by a null character(‘\0’).

C Library String Functions

  • These functions are defined in the cstring header.
  • The pointer passed to these routines must point to null-terminated array.
char str[] = { 'C', '+', '+' };
cout << strlen(str);  // This output: 6
  • strlen will keep looking through the memory that follows str until it encounters a null character.

Comparing Strings

  • Compare two library strings: use normal relational or equality operators. Using these operators on C-style strings compares the pointer values, not the strings themselves.
string str1 = "gao", str2 = "gaoxiang";
cout << "Larger: " << ((str1 > str2) ? str1 : str2) << '\n';  // gaoxiang
char arr1[] = "gao", arr2[] = "gaoxiang";
cout << "Larger: " << ((arr1 > arr2) ? arr1 : arr2) << '\n';  // Undefined.
  • We call strcmp to compare the strings. That function returns 0 if the strings are equal, or a positive or negative value, depending on whether the first string is larger or smaller than the second.
    if (strcmp(ca1, ca2) < 0) // same effect as string comparison s1 < s2

Caller Is Responsible for Size of a Destination String

  • To use strcat and strcpy, we must pass an array to hold the resulting string. The array we pass must be large enough to hold the generated string, including the null character at the end.
// disastrous if we miscalculated the size of str
strcpy(str, ca1);   // copies ca1 into str
strcat(str, " ");   // adds a space at the end of str
strcat(str, ca2);   // concatenates ca2 onto str

Exercises Section 3.5.4

Exercise 3.37

What does the following program do?

const char ca[] = {'h', 'e', 'l', 'l', 'o'};
const char *cp = ca;
while (*cp)
{
    cout << *cp << endl;
    ++cp;
}
  • Print from ca to the first ‘\0’

Exercise 3.38

In this section, we noted that it was not only illegal but meaningless to try to add two pointers. Why would adding two pointers be meaningless?

  • Because it generates a meaningless address.

Exercise 3.39

Write a program to compare two strings. Now write a program to compare the values of two C-style character strings.

#include <iostream>
#include <string>
#include <cstring>

using std::cout;
using std::string;

int main()
{
    string str1 = "gao", str2 = "gaoxiang";
    cout << "Larger: " << ((str1 > str2) ? str1 : str2) << '\n';  // gaoxiang

    char arr1[] = "gaoxiang", arr2[] = "gao";
    cout << "Larger: " << (strcmp(arr1, arr2) > 0 ? arr1 : arr2) << '\n';  // gaoxiang

    return 0;
}

Exercise 3.40

Write a program to define two character arrays initialized from string literals. Now define a third character array to hold the concatenation of the two arrays. Use strcpy and strcat to copy the two arrays into the third.

#include <iostream>
#include <cstring>

using std::cout;

int main()
{
    char arr1[] = "gao", arr2[] = "xiang", arr3[9];
    strcat(arr1, arr2);
    strcpy(arr3, arr1);
    cout << arr3 << '\n';

    return 0;
}
/*
Output:
gaoxiang
*/

3.5.5. Interfacing to Older Code

Mixing Library strings and C-Style Strings

  • We can use a null-terminated character array:
    1. To initialize or assign a string.
    2. As one operand(but not both operands) to the string addition operator
    3. As the right-hand operand in the string compound assignment (+=) operator.
#include <iostream>
#include <string>

using std::cout;
using std::string;

int main()
{
    char arr[] = "gaoxiangnumber1";
    string str1 = arr;
    string str2 = "hello ";
    string str3 = str2 + arr;
    str2 += arr;
    cout << str1 << '\n' << str2 << '\n' << str3;

    return 0;
}
/*
Output:
gaoxiangnumber1
hello gaoxiangnumber1
hello gaoxiangnumber1
*/
  • We can’t direct initialize a character pointer from a string. A string member function c_str returns a pointer(type: const char*) to the beginning of a null-terminated character array that holds the same data as the characters in the string. The array returned by c_str is not guaranteed to be valid. Any subsequent use of str that might change the value of str can invalidate this array.
string str = "gaoxiangnumber1";
// error: initializer fails to determine size of ‘arr1’, array must be initialized with a brace-enclosed initializer
char arr1[] = str;

// error: initializer fails to determine size of ‘arr2’, array must be initialized with a brace-enclosed initializer
char arr2[] = str.c_str();
// error: invalid conversion from ‘const char*’ to ‘char*’
char *arr3 = str.c_str();
const char *arr4 = str.c_str();  // ok

Using an Array to Initialize a vector

  • We cannot: initialize a built-in array from another array; initialize an array from a vector.
  • We can use an array to initialize a vector: specify the address of the first element and one past the last element that we want to copy. The specified range can be a subset of the array:
// copies three elements: arr[1], arr[2], arr[3]
vector<int> vec(arr + 1, arr + 4);

Exercises Section 3.5.5

Exercise 3.41

Write a program to initialize a vector from an array of ints.

#include <iostream>
#include <vector>

using std::cout;
using std::begin;
using std::end;
using std::vector;

template<typename T>
void Print(vector<T> &vec)
{
    typename vector<T>::const_iterator it1 = vec.cbegin(), it2 = vec.cend();
    for(; it1 != it2; ++it1)
    {
        cout << *it1 << " ";
    }
    cout << ((vec.cbegin() == vec.cend()) ? "empty\n" : "\n");
}

int main()
{
    int arr[] = {1, 2, 3, 4, 5};
    vector<int> vec(begin(arr), end(arr));
    Print(vec);

    return 0;
}
/*
Output:
1 2 3 4 5
*/

Exercise 3.42

Write a program to copy a vector of ints into an array of ints.

#include <iostream>
#include <vector>

using std::vector;

int main()
{
    vector<int> vec(10, 5);
    int length = vec.size();
    int arr[length];
    for(int index = 0; index < length; ++index)
    {
        arr[index] = vec[index];
    }

    return 0;
}

3.6. Multidimensional Arrays

Initializing the Elements of a Multidimensional Array

  • Multidimensional arrays may be initialized by specifying bracketed values for each row.
int ia[3][4] =          // three elements; each element is an array of size 4
{
    {0, 1, 2, 3},       // initializers for the row indexed by 0
    {4, 5, 6, 7},       // initializers for the row indexed by 1
    {8, 9, 10, 11}  // initializers for the row indexed by 2
};
  • The nested braces are optional.
// equivalent initialization without the optional nested braces for each row
int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
  • Elements may be left out of the initializer list. We can initialize only the first element of each row as follows:
// initialize [0] in each row, remaining elements are value initialized(§3.5.1,p. 114).
int ia[3][4] = {{ 0 }, { 4 }, { 8 }};
// explicitly initialize row 0; the remaining elements are value initialized
int ix[3][4] = {0, 3, 6, 9};

Subscripting a Multidimensional Array

  • If an expression provides as many subscripts as there are dimensions, we get an element with the specified type.
    If we supply fewer subscripts than there are dimensions, then the result is the inner-array element at the specified index.
int arr1[10][20][30];
int arr2[3][4];
// Assign the first element of arr1 to the last element in the last row of arr2
arr2[2][3] = arr1[0][0][0];
int (&ref)[4] = arr2[1];  // Bind ref to the second four-element array in arr2

Using a Range for(C++11) with Multidimensional Arrays

int arr[3][4];
size_t cnt = 0;
for(int (&row)[4] : arr)
{
    for(int &col : row)
    {
        col = cnt++;
    }
}
  • We want to change the value of the elements, so we declare our control variables as references(§ 3.2.3, p. 93). The first for iterates through the elements in arr which are arrays of size 4. The second for iterates through one of those 4-element arrays.
for(const auto &row : arr)
{
    for(auto col : row)
    {
        cout << col << '\n';
    }
}
  • We define the control variable of the outer loop as a reference in order to avoid the array to pointer conversion(§ 3.5.3, p. 117).
for(auto row : arr)
{
    // error: no matching function for call to ‘begin(int*&)’
    for(auto col : row)
    {
        cout << col << '\n';
    }
}
  • Because row is not a reference, when the compiler initializes row, it will convert each array element to a pointer to that array’s first element. So, the type of row is int*. The inner for loop attempts to iterate over an int*, thus illegal.
  • To use a multidimensional array in a range for, the loop control variable for all but the innermost array must be references.

Pointers and Multidimensional Arrays

  • When we use the name of a multidimensional array, it is automatically converted to a pointer to the first element in the array.
  • Because a multidimensional array is an array of arrays, the pointer type to which the array converts is a pointer to the first inner array:
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

C++11: Using auto or decltype to Simplify Declarations

#include <iostream>

using std::cout;
using std::begin;
using std::end;

int main()
{
    int arr[2][3] = {1, 2, 3, 4, 5, 6};
    for (auto p = arr; p != arr + 2; ++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 + 3; ++q)
        {
            cout << *q << ' ';
        }
        cout << '\n';
    }

    for (auto p = begin(arr); p != end(arr); ++p)
    {
        // q points to the first element of an array of four ints; that is, q points to an int
        for (auto q = begin(*p); q != end(*p); ++q)
        {
            cout << *q << ' ';
        }
        cout << '\n';
    }

    return 0;
}
/*
1 2 3
4 5 6
1 2 3
4 5 6
*/
#include <iostream>

using std::cout;

int main()
{
    int arr[2][3] = {1, 2, 3, 4, 5, 6};
    // error: cannot convert ‘int (*)[3]’ to ‘int*’ in initialization
    //int *ptr1 = arr;
    int (*ptr1)[3] = arr;
    cout << arr << '\t' << arr[0] << '\t' << &arr[0][0] << '\t' << arr[0][0] << '\n'
         << ptr1 << '\t' << *ptr1 << '\t' << **ptr1 << '\n';

    cout << "\nWrong traverse because ptr1 is int(*)[3],"
         "++ptr1 move from [0][0] to [1][0], not to [0][1]\n";
    for(int index = 0; index < 6; ++index, ++ptr1)
    {
        cout << ptr1 << '\t' << *ptr1 << '\t' << **ptr1 << '\n';
    }

    cout << "\nRight traverse:\n";
    for(ptr1 = arr; ptr1 != arr + 2; ++ptr1)
    {
        cout << "Row: " << ptr1 << " | ";
        for(int *ptr2 = *ptr1; ptr2 != *ptr1 + 3; ++ptr2)
        {
            cout << ptr2 << ' ' << *ptr2 << ' ';
        }
        cout << '\n';
    }

    return 0;
}
/*
Output:
0x7ffd15980030  0x7ffd15980030  0x7ffd15980030  1
0x7ffd15980030  0x7ffd15980030  1

Wrong traverse because ptr1 is int(*)[3],++ptr1 move from [0][0] to [1][0], not to [0][1]
0x7ffd15980030  0x7ffd15980030  1
0x7ffd1598003c  0x7ffd1598003c  4
0x7ffd15980048  0x7ffd15980048  4196256
0x7ffd15980054  0x7ffd15980054  0
0x7ffd15980060  0x7ffd15980060  0
0x7ffd1598006c  0x7ffd1598006c  32600

Right traverse:
Row: 0x7ffd15980030 | 0x7ffd15980030 1 0x7ffd15980034 2 0x7ffd15980038 3
Row: 0x7ffd1598003c | 0x7ffd1598003c 4 0x7ffd15980040 5 0x7ffd15980044 6
*/

Type Aliases Simplify Pointers to Multidimensional Arrays

#include <iostream>

using std::cout;

using int_array = int[3];  // Equivalent to: typedef int int_array[3]

int main()
{
    int arr[2][3] = {1, 2, 3, 4, 5, 6};
    for(int_array *ptr1 = arr; ptr1 != arr + 2; ++ptr1)
    {
        for(int *ptr2 = *ptr1; ptr2 != *ptr1 + 3; ++ptr2)
        {
            cout << *ptr2 << ' ';
        }
        cout << '\n';
    }

    return 0;
}
/*
1 2 3
4 5 6
*/

Exercises Section 3.6

Exercise 3.43

Write three different versions of a program to print the elements of ia. One version should use a range for to manage the iteration, the other two should use an ordinary for loop in one case using subscripts and in the other using pointers. In all three programs write all the types directly. That is, do not use a type alias, auto, or decltype to simplify the code.

#include <iostream>

using std::cout;

int main()
{
    int arr[3][4] = {{ 0, 1, 2, 3 }, { 4, 5, 6, 7 }, { 8, 9, 10, 11 }};

    cout << "Version 1: Range for\n";
    for(int (&row)[4] : arr)
    {
        for(int &col : row)
        {
            cout << col << ' ';
        }
    }

    cout << "\nVersion 2: Ordinary for using subscripts\n";
    for(int index1 = 0; index1 < 3; ++index1)
    {
        for(int index2 = 0; index2 < 4; ++index2)
        {
            cout << arr[index1][index2] << ' ';
        }
    }

    cout << "\nVersion 3: Ordinary for using pointers\n";
    for(int (*row)[4] = arr; row != arr + 3; ++row)
    {
        for(int *col = *row; col != *row + 4; ++col)
        {
            cout << *col << ' ';
        }
    }

    return 0;
}
/*
Version 1: Range for
0 1 2 3 4 5 6 7 8 9 10 11
Version 2: Ordinary for using subscripts
0 1 2 3 4 5 6 7 8 9 10 11
Version 3: Ordinary for using pointers
0 1 2 3 4 5 6 7 8 9 10 11
*/

Exercise 3.44

Rewrite the programs from the previous exercises using a type alias for the type of the loop control variables.

#include <iostream>

using std::cout;

using int_array = int[4];

int main()
{
    int arr[3][4] = {{ 0, 1, 2, 3 }, { 4, 5, 6, 7 }, { 8, 9, 10, 11 }};

    for(int_array *row = arr; row != arr + 3; ++row)
    {
        for(int *col = *row; col != *row + 4; ++col)
        {
            cout << *col << ' ';
        }
    }

    return 0;
}
/*
0 1 2 3 4 5 6 7 8 9 10 11
*/

Exercise 3.45

Rewrite the programs again, this time using auto.

#include <iostream>

using std::cout;

int main()
{
    int arr[3][4] = {{ 0, 1, 2, 3 }, { 4, 5, 6, 7 }, { 8, 9, 10, 11 }};

    for(auto &row : arr)
    {
        for(auto col : row)
        {
            cout << col << ' ';
        }
    }

    return 0;
}
/*
0 1 2 3 4 5 6 7 8 9 10 11
*/

Chapter Summary

Please indicate the source: http://blog.csdn.net/gaoxiangnumber1

Welcome to my github: https://github.com/gaoxiangnumber1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值