C++ Primer(第五版)读书笔记 & 习题解答 --- Chapter 3

Chapter 3.1

1. using声明具有如下的形式:

using namespace::name;

 

Chapter 3.2

1. C++标准一方面对库类型所提供的操作做了规定,另一方面也对库的实现做出了性能上的要求,所以,在一般的应用场合,标准库类型都有足够的效率。

2. 当用一个字符串字面值初始化string对象时,除了最后那个空字符外其他所有的字符都会被拷贝到新创建的string对象中去。

3. 如果使用等号初始化一个变量,实际上执行的是拷贝初始化。如果不使用等号,则执行的是直接初始化:

string s1 = "hi, ya"; // 拷贝初始化
string s2(10, 'c');   // 直接初始化

4. 在执行读取string的操作时,string对象会自动忽略开头的空白(即空格符、换行符、制表符等)并从第一个真正的字符开始读起,直到遇见下一处空白为止:

// 如果我们输入"      Hello World!         ",输出将是"Hello",没有任何空格
string s;
cin >> s;
cout << s << endl;

5. string::size_type是一个无符号类型的值,而且能足够存放下任何string对象的大小。需要注意的是,string类的size函数的返回值类型是string::size_type,由于它是一个无符号类型的值,所以切勿在表达式中将它与有符号类型混用,因为有符号类型会被自动地转换成无符号类型,导致不容易发现的错误。

6. 当把string对象和字符字面值及字符串字面值混在一条语句中使用时,必须确保每个加法运算符的两侧的运算对象至少有一个是string:

string s1 = "hello";
string s2 = s1 + ", world!"; // 正确
string s3 = "hello" + ", world" + s1; // 错误,不能把字面值直接相加

7. 因为某些历史原因,也为了与C兼容,所以C++语言中的字符串字面值并不是标准库类型string,切记,它们是不同的类型。

8. C++11提供了一种新的语句:range for语句,这种语句遍历给定序列中的每个元素并对每个元素执行某种操作,语法形式是:

for ( declaration : expression )
{
    statement
}

其中,expression部分表示一个序列对象,declaration部分负责定义一个变量,用于访问序列中的元素,每次迭代,declaration定义的变量就会被初始化为expression部分的下一个元素值:

string str = "Hello";
for ( auto c : str )
{
    std::cout << c << std::endl;
}

 

Chapter 3.3

1. 模板本身不是类或函数,可以将模板看作为编译器生成类或函数编写的一份说明。编译器根据模板创建类或函数的过程称为实例化。

2. vector能容纳绝大多数类型的对象作为其元素,但是因为引用不是对象,所以不存在包含引用的vector。

3. C++11提供了一种新的初始化vector的方法,称为列表初始化:

vector<int> values = { 1, 2, 3, 4 };

4. 我们可以只提供vector对象容纳的元素数量而省去初始值:

vector<int> values(10); // 创建10个int类型的对象

此时库会创建一个值初始化的元素初值,并把它赋给容器中的所有元素。所谓值初始化,就是针对内置类型,将会被初始化为0。而针对类类型,将会使用默认构造函数进行初始化。

5. 如果在初始化vector时使用了列表初始化的方式,但是提供的值又不能用来执行列表初始化,那么编译器会尝试用其他方式来初始化vector:

vector<string> v1{ "hello" };     // 列表初始化
// 无法执行列表初始化,因为int对象10不能作为string对象的初始值,编译器将构造有10个值为hello的string对象的vector
vector<string> v2{ 10, "hello" };

6. C++标准要求vector应该能在运行时高效快速地添加元素。因此既然vector对象能高效地增长,那么在定义vector对象的时候设定其大小也就没有什么必要了,事实上如果这么做可能性能反而更差。

7. 需要注意的是,如果循坏体内部包含有向vector对象添加元素的语句,则不能使用范围for循环,并且在使用其他循环时,要确保所写的循环对于vector大小的处理正确无误。

8. 只有当元素的值可比较时,vector对象才能被比较。

 

Chapter 3.4

1. 所有标准库容器都可以使用迭代器,但是其中只有少数几种才同时支持下标运算符。

2. 迭代器有有效和无效之分:有效的迭代器或者指向某个元素,或者指向容器中尾元素的下一位置。其他所有情况都属于无效。

3. end函数返回的迭代器常被称为尾后迭代器,它并不实际指示某个元素。

4. 试图解引用一个无效的迭代器或者一个尾后迭代器都是导致未定义的行为。

5. 所有标准库容器的迭代器都定义了==和!=运算符,但是它们中的大多数都没有定义<运算符。所以,尽可能在使用迭代器时养成使用==和!=运算符的习惯。

6. begin和end运算符返回的具体类型由对象是否是常量决定,如果对象是常量,则返回const_iterator;否则就返回iterator。在C++11中,引入了两个新函数,cbegin和cend,无论对象是否是常量,cbegin和cend都返回const_iterator。

7. 任何一种可能改变vector对象元素数量的操作,都可能使该vector对象的迭代器失效。

8. 两个迭代器相减的结果的类型是名为difference_type的带符号整型数。

 

Chapter 3.5

1. 数组是一种复合类型,声明形式如a[b],a是数组的名字,b是数组的维度。维度也属于数组类型的一部分,编译时维度必须是已知的,也就是说,维度必须是一个常量表达式。

2. 定义数组的时候必须指定数组元素的类型,不允许使用auto关键字由初始值列表推断数组元素的类型。

3. 数组中的元素为对象,所以不存在引用的数组。默认情况下,数组中的元素执行默认初始化。

4. 当用列表初始化数组时,如果没有指定维度,编译器会根据初始值的数量计算并推测出维度。如果指定了维度,那么初始值的数量不能超过维度指定的大小。如果维度比列表中的初始值数量大 ,则用提供的初始值初始化靠前的元素,剩下的元素执行值初始化:

const unsigned sz = 3;
int a1[sz] = { 0, 1, 2 };
int a2[] = { 0, 1, 2 }; // 维度是3的数组
int a3[5] = { 0, 1, 2}; // 等价于a3[] = { 0, 1, 2, 0, 0 };
string a4[3] = { "hi", "bye" }; // 等价于a4[3] = { "hi", "bye", "" };
int a5[2] = { 0, 1, 2 }; // 错误,初始值过多

5. 当使用字符串字面值初始化字符数组时,要注意在字符串字面值的结尾处还有一个空字符:

char a1[] = "C++"; // a1包含四个字符,分别是'C' '+' '+' '\0'
char a2[6] = "Daniel"; // 错误,没有空间可以存放结尾的空字符

6. 在使用数组下标的时候,通常将其定义为size_t类型,它是一种机器相关的无符号类型,被设计得足够大以便能表示内存中任意对象的大小。

7. 在很多用到数组名字的地方,编译器都会自动地将其替换为一个指向数组首元素的指针:

int a[] = { 1, 2, 3 };
int *p = a; // 等价于int *p = &a[0];

这一事实有很多含义,其中之一是当使用数组作为一个auto变量的初始值时,得到的类型是指针而非数组:

int a[] = { 1, 2 };
auto a2(a); // a2的类型是int*

而当使用decltype声明变量时,转换则不会发生:

int a[] = { 1, 2 };
decltype(a) a2 = { 2, 3 }; // a2是一个含有2个元素的数组

8. C++11引入了两个名为begin和end的函数,它们的功能与标准库容器中的begin和end类似:

int a[] = { 1, 2, 3 };
int *beg = begin(a); // beg指向数组a的首元素
int *last = end(a);  // last指向数组a的尾元素的下一位置

9. 两个指向同一个数组当中的元素的指针相减的结果类型是一种名为ptrdiff_t的标准库类型,它是一种与机器实现有关的有符号整数类型,它的空间足够大,能够表示任意两个指向同一个数组当中的元素的指针之间的距离。

10. 可以使用下标运算来访问数组中的元素,它与标准库类型string和vector的下标运算的区别在于:标准库类型限定使用的下标必须是无符号类型,而数组的下标运算没有这个要求:

int arr[] = { 0, 2, 4 };
int *p = &arr[2];
int k = p[-2]; // p[-2]是arr[0]表示的那个元素

11. string对象提供了成员函数c_str用于返回一个以空字符结尾的字符数组,需要注意的是,无法保证c_str返回的数组一直有效,任何后续的对string对象的修改都可能让它变得无效。

 

Chapter 3.6

1. 可以使用下标运算符来访问多维数组的元素,此时数组的每个维度对应一个下标运算符。如果表达式含有的下标运算符数量和数组的维度一样多,该表达式的结果将是给定类型的元素;反之,如果表达式含有的下标运算符数量比数组的维度小,则表达式的结果将是给定索引处的一个内层数组:

int arr_0[3][4] = { 0 };
int arr_1[10][20][30] = { 0 };
arr_0[0][1] = arr_1[1][2][3]; // 把arr_1中的某个元素赋值给arr_0中的某个元素
int (&arr_2)[4] = arr_0[0]; // 把arr_2绑定到arr_0的第一个4元素数组上

2. 当使用范围for语句处理多维数组时,除了最内层的循坏外,其他所有循坏的控制变量都应该是引用类型,这是因为如果不用引用类型,编译器会初始化控制变量为指向数组首元素的指针:

int arr[2][3] = { 0 };
for ( auto row : arr )
{
    for ( auto col : row )
    {
    }
}
// 上面的程序将无法通过编译,因为row将被初始化为指向数组arr首元素的指针,因此导致auto col : row语句是非法的,所以记得使用引用类型:
for ( const auto &row : arr )
{
    for ( auto col : row )
    {
    }
}

 

Exercises Section 3.2.2

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

A_1. 

#include <iostream>
#include <string>

int main()
{
    std::string str_1, str_2;
    std::cin >> str_1 >> str_2;
    if ( str_1 == str_2 )
    {
        std::cout << "str_1 and str_2 are equal" << std::endl;
    }
    else
    {
        if ( str_1 > str_2 )
        {
            std::cout << "str_1 is larger" << std::endl;
        }
        else
        {
            std::cout << "str_2 is larger" << std::endl;
        }
    }

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

int main()
{
    std::string str_1, str_2;
    std::cin >> str_1 >> str_2;
    if ( str_1.size() == str_2.size() )
    {
        std::cout << "str_1 and str_2 have the same length" << std::endl;
    }
    else
    {
        if ( str_1.size() > str_2.size() )
        {
            std::cout << "str_1 is longer" << std::endl;
        }
        else
        {
            std::cout << "str_2 is longer" << std::endl;
        }
    }

    return 0;
}

 

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

A_2. 

#include <iostream>
#include <string>

int main()
{
    std::string str_1, str_2;
    while ( std::cin >> str_1 )
    {
        str_2 += str_1;
    }

    std::cout << str_2;

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

int main()
{
    std::string str_1, str_2;
    std::cin >> str_2;    // 读取第一个字符串,防止在第一个字符串之前出现空格
    while ( std::cin >> str_1 )
    {
        str_2 = str_2 + ' ' + str_1;
    }

    std::cout << str_2;

    return 0;
}

  

Exercises Section 3.2.3

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

A_1. 

#include <iostream>
#include <string>

int main()
{
    std::string str = "Hello";
    for ( auto &c : str )
    {
        c = 'X';
    }
    std::cout << str << std::endl;

    return 0;
}

 

Q_2. Rewrite the program in the first exercise, first using a while and again using a traditional for loop.

A_2. 

#include <iostream>
#include <string>

int main()
{
    std::string str = "Hello";
    decltype(str.size()) index = 0;
    while ( index < str.size() )
    {
        str[index++] = 'X';
    }
    std::cout << str << std::endl;

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

int main()
{
    std::string str = "Hello";
    for ( decltype(str.size()) index = 0; index < str.size(); ++index )
    {
        str[index] = 'X';
    }
    std::cout << str << std::endl;

    return 0;
}

 

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

A_3. 

#include <iostream>
#include <string>
#include <cctype>

int main()
{
    std::string str;
    if ( std::cin >> str )
    {
        std::string newStr;
        for ( const auto &c : str )
        {
            if ( !std::ispunct(c) )
            {
                newStr += c;
            }
        }

        std::cout << newStr << std::endl;
    }

    return 0;
}

 

Exercises Section 3.3.2

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

A_1.

#include <iostream>
#include <vector>

int main()
{
    int value;
    std::vector<int> vInts;
    while ( std::cin >> value )
    {
        vInts.push_back( value );
    }

    return 0;
}

 

Exercises Section 3.3.3

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

A_1.

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

int main()
{
    std::string value;
    std::vector<std::string> vWords;
    while ( std::cin >> value )
    {
        vWords.push_back( value );
    }

    for ( auto &rWord : vWords )
    {
        for ( auto &rChar : rWord )
        {
            rChar = toupper( rChar );
        }
    }

    return 0;
}

 

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

A_2.

#include <iostream>
#include <vector>

int main()
{
    int value;
    std::vector<int> vInts;
    while ( std::cin >> value )
    {
        vInts.push_back( value );
    }

    // 如果没有元素或只有一个元素...
    if ( vInts.size() <= 1 )
    {
        // ...直接返回
        return 0;
    }

    for ( std::vector<int>::size_type index = 0; index < vInts.size() - 1; ++index )
    {
        std::cout << "sum: " << vInts[index] + vInts[index + 1] << std::endl;    
    }

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

int main()
{
    int value;
    std::vector<int> vInts;
    while ( std::cin >> value )
    {
        vInts.push_back( value );
    }

    // 如果没有元素或只有一个元素...
    if ( vInts.size() <= 1 )
    {
        // ...直接返回
        return 0;
    }

    for ( std::vector<int>::size_type index = 0; (index * 2) < vInts.size(); ++index )
    {
        std::cout << "sum: " << vInts[index] + vInts[vInts.size() - 1 - index] << std::endl;    
    }

    return 0;
}

 

Exercises Section 3.4.1

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

A_1. 

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> vInts{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    for ( auto iter = vInts.begin(); iter != vInts.end(); ++iter )
    {
        (*iter) *= 2;
    }

    for ( auto value : vInts )
    {
        std::cout << value << std::endl;
    }

    return 0;
}

 

Exercises Section 3.4.2

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

A_1. 

#include <iostream>
#include <vector>

int main()
{
    int value;
    std::vector<int> vInts;
    while ( std::cin >> value )
    {
        vInts.push_back( value );
    }

    // 如果没有元素或只有一个元素...
    if ( vInts.size() <= 1 )
    {
        // ...直接返回
        return 0;
    }

    for ( auto iter = vInts.cbegin(); iter != vInts.end() - 1; ++iter )
    {
        std::cout << "sum: " << (*iter) + *(iter + 1) << std::endl;    
    }

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

int main()
{
    int value;
    std::vector<int> vInts;
    while ( std::cin >> value )
    {
        vInts.push_back( value );
    }

    // 如果没有元素或只有一个元素...
    if ( vInts.size() <= 1 )
    {
        // ...直接返回
        return 0;
    }

    for ( auto iter = vInts.cbegin(), rIter = vInts.cend() - 1; iter <= rIter; ++iter, --rIter ) 
    {
        std::cout << "sum: " << (*iter) + (*rIter) << std::endl;    
    }


    return 0;
}

 

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

A_2. 

#include <iostream>
#include <vector>

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

    return 0;
}

 

Exercises Section 3.5.2

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

A_1. 

int main()
{
    int values[10];
    for ( int index = 0; index < 10; ++index )
    {
        values[index] = index;
    }

    return 0;
}

 

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

A_2. 

int main()
{
    int values[10];
    for ( int index = 0; index < 10; ++index )
    {
        values[index] = index;
    }

    int copyTo[10];
    for ( int index = 0; index < 10; ++index )
    {
        copyTo[index] = values[index];
    }

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

int main()
{
    std::vector<int> values( 10 );
    for ( int index = 0; index < 10; ++index )
    {
        values[index] = index;
    }

    std::vector<int> copyTo( values );

    return 0;
}

 

Exercises Section 3.5.3

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

A_1.

#include <iostream>
#include <iterator>

int main()
{
    const unsigned ARRAY_SIZE = 10;
    int arr[ARRAY_SIZE];

    int *pValue = std::begin( arr );
    while ( pValue != std::end(arr) )
    {
        *pValue = 0;
        ++pValue;
    }

    return 0;
}

 

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

A_2.

#include <iostream>
#include <iterator>

int main()
{
    int arr_0[] = { 0, 1, 2 };
    int arr_1[] = { 0, 1, 2, 4 };

    int *pValue_0 = std::begin( arr_0 );
    int *pValue_1 = std::begin( arr_1 );
    while ( (pValue_0 != std::end(arr_0)) &&
            (pValue_1 != std::end(arr_1)) &&
            (*pValue_0 == *pValue_1) )
    {
        ++pValue_0;
        ++pValue_1;
    }

    if ( (pValue_0 == std::end(arr_0)) && (pValue_1 == std::end(arr_1)) )
    {
        std::cout << "数组相等" << std::endl;
    }
    else
    {
        std::cout << "数组不相等" << std::endl;
    }

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

int main()
{
    std::vector<int> vInts_0{ 0, 1, 2, 3 };
    std::vector<int> vInts_1{ 1, 1, 2 };

    if ( vInts_0 == vInts_1 )
    {
        std::cout << "vector相等" << std::endl;
    }
    else
    {
        std::cout << "vector不相等" << std::endl;
    }

    return 0;
}

 

Exercises Section 3.5.4

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

A_1. 

#include "stdafx.h"
#include <iostream>

int main()
{
    const char *pszValue_0 = "Hello ";
    const char *pszValue_1 = "World!";

    char *pszFinalValue = new char[strlen(pszValue_0) + strlen(pszValue_1) + 1];
    strcpy( pszFinalValue, pszValue_0 );
    strcat( pszFinalValue, pszValue_1 );

    delete[] pszFinalValue;
    pszFinalValue = NULL;

    return 0;
}

 

posted @ 2015-02-24 19:48  EnigmaJJ  阅读(1484)  评论(3编辑  收藏  举报