10.7 练习
部分练习题的答案可以在本书的电子文档“Annotated Solution Guide for Thinking in C++”中找到,只需支付很少的费用就可以从http://www.BruceEckel.com得到这个电子文档。
10-1 创建一个函数(带一个默认值为零的参数),函数内有一个静态变量的,这个静态变量是一个指针。当调用者为这个参数提供值时,它就指向一个整形数组的起始地址。如果用默认的参数值调用该函数,那么这个函数就返回数组的下一个值,直到它访问到数组中的“-1”(在数组中,-1作为结束的标志),在函数main()中调用这个函数。
10-2 创建一个这样的函数:每调用一次,它就返回Fibonacci序列中的下一个值。增加一个bool类型的参数,其默认值为false,当传递给该参数的值为true时重置函数使它指向Fibonacci序列的开头。在函数main()中调用这个函数。
10-3 创建一个有一个整型数组的类。在类内部用静态整型常量设置数组的大小。增加一个const int变量,并在构造函数初始化列表中初始化。构造函数是内联的。增加一个static int成员变量并用特定值来初始化。增加一个内联的成员函数print(),它打印数组中所有数组元素值并调用静态成员函数。在main()中运用这样的类。
10-4 创建一个类Monitor,它能知道它的成员函数incident()被调用了多少次。增加一个成员函数print()显示incident()被调用的次数,再创建一个包含一个静态的Monitor类的对象的函数。每次调用该函数时,它都会调用print()成员函数显示incident()被调用的次数。在主函数main()中调用这个函数。
10-5 修改练习4中的Monitor类,使其成员函数decrement()被调用时会减少记数。另创建一个类Monitor2,它的构造函数有一个指向Monitor1的指针参数,该构造函数存储指针值,调用incident()以及print()。Monitor2的析构函数调用decrement()和print()。写一个函数,在该函数中创建一个Monitor2的静态对象。在main()中测试调用该函数和不调用该函数时,Monitor2的析构函数各会出现什么结果。
10-6 定义一个Monitor2类的全局对象,看看会得到什么结果。
10-7 创建一个类,它的析构函数打印信息并调用exit(),定义该类的一个全局对象,看看会得到什么结果。
10-8 在文件StaticDestructors.cpp中,在main()内用不同的顺序调用f()、g()来检验构造函数与析构函数的调用顺序,你的编译器能正确地编译它们吗?
10-9 在文件StaticDestructors.cpp中,把out的最初定义变为一个extern声明,并把实际定义放到a(它的Obj构造函数传送信息给out)的定义之后,看看默认的错误处理是怎样工作的。运行程序时应确保没有其他重要程序在运行,否则机器会出现错误。
10-10 验证当带有多个静态变量的头文件被多个cpp文件包含时,不会有名字冲突。
10-11 创建一个简单的类,它包含一个整型数据成员,一个用自身参数初始化该数据成员的构造函数,还有一个用自身参数设置该成员值的成员函数,以及打印该成员值的print()函数。把该类放到头文件中去,在两个cpp文件中包含该头文件,在一个头文件中创建类的一个实例,在另外一个类中用extern声明,并在main()中测试。记住必须连接两个对象文件,否则连接器将找不到所要连接的目标。
10-12 创建练习11中的类的静态实例,并验证:由于不存在this指针,连接器找不到它。
10-13 在一个头文件中声明一个函数。在另一个cpp文件中定义它,在第二个cpp文件的main()中调用这个函数,编译并验证它能正常运行。然后改变函数的定义,使它变为静态,验证连接器将找不到这个函数。
10-14 修改第8章中的Volatile.cpp文件,使comm:isr()能够像中断服务例程一样运行。注意:中断服务例程不带任何参数。
10-15 写一个使用auto和register关键字的简单程序,然后编译它。
10-16 创建一个包含一个名字空间的头文件。在名字空间里声明几个函数。再创建另一个头文件,它包含第一个头文件,并在先前的名字空间的基础上再增加几个函数声明。写一个包含第二个头文件的cpp文件。把名字空间用一个短的别名代替。在函数定义里使用作用域运算符调用这些函数。在另外一个单独的函数里,通过using指令把名字空间引入到函数中。并证实:这时并不需要作用域运算符调用名字空间里的函数。
10-17 创建一个带无名的名字空间的头文件。在两个单独的cpp文件中包含这个头文件,验证这个无名的名字空间对于这两个翻译单元来说都是一致的。
10-18 使用练习17的头文件,验证无名名字空间中的名字在一个翻译单元里即使不加指定也是可见的。
10-19 修改FriendInjection.cpp文件,增加一个友元函数的定义,在主函数main()中调用它。
10-20 在文件Arithmetic.cpp中,说明在一个函数中使用的using指令并不能扩展到这个函数的范围之外。
10-21 修改文件OverridingAmbiguity.cpp,先使用作用域运算符,然后用using声明代替作用域运算符来强迫编译器选择其中某个同名的函数名。
10-22 在两个头文件中,创建两个名字空间,每一个名字空间都包含一个类,且类名相同。创建一个包含这两个头文件的cpp文件。定义一个函数,在该函数中用using指令引入两个名字空间,然后创建类的一个对象,看看会有什么发生。再改变using指令的使用,使它为全局使用(在函数之外),看看结果是否不同。另外再使用作用域运算符,并创建两个类的对象。
10-23 用using声明修改练习22的程序,强迫编译器选择其中某个同名的类名。
10-24 去掉文件BobsSuperDuperLibrary.cpp和UnnamedNamespaces.cpp中的名字空间声明,把这些声明放到一个单独的头文件中,在处理过程中给这个无名的名字空间一个名字。在第三个文件中创建一个新的名字空间,该名字空间使用using声明包含其他两个名字空间。在主函数main()中使用using指令引用这个新的名字空间并访问所有的名字空间。
10-25 创建一个包含<string>和<iostream>的头文件,但不使用任何using指令和using声明。就像本书中所看到的一样,这里使用“include”。创建一个带有内联函数的类,它含有一个string成员,一个用自身参数初始化该成员的构造函数,还含有一个print()函数,它显示String成员的值,写一个cpp文件并在main()中运用这个类。
10-26 创建一个带static double和long类型的成员的类,写一个静态的成员函数并打印出这些静态数据成员的值。
10-27 创建一个类,它包含一个整型数据成员,一个通过自身参数初始化该整型数据成员的构造函数,还有一个显示这个整型数据成员的print()函数。再创建一个类,它包含第一个类的静态对象,增加一个静态成员函数并调用这个静态对象的print()函数,在主函数main()中运用这个类。
10-28 创建一个类,包含常量的和非常量的静态整型数组。写静态的方法来打印这些数组的值。在main()函数中运用这些类。
10-29 创建一个类,它包含一个string类型的数据成员,一个通过自身参数初始化该数据成员的构造函数,还有一个显示这个数据成员的print()函数,再创建一个类,它包含第一个类的对象的const和非const的静态对象数组,还有打印这些数组的静态方法。在main()函数中运用第二个类。
10-30 创建一个带整型成员和一个默认构造函数的结构(struct),默认的构造函数把整型成员初始化为零。让这个结构局部于一个函数。在该函数中,创建一个该结构的对象数组,并演示这个数组中的整型被自动初始化为零。
10-31 创建一个类,它体现指针连接,但只允许使用一个指针。
10-32 在一个头文件中,创建一个类Mirror,它包含两个数据成员:一个是指向Mirror对象的指针和一个bool类型的数据成员,写两个构造函数:一个是默认的构造函数,它把bool成员初始化为true,使Mirror指向零值。第二个构造函数带有一个指向Mirror对象的指针参数,用该参数给对象的指针赋值。并把bool类型的数据成员设置成false。再增加一个成员函数test(),如果对象的指针成员为非零,则通过指针调用test()并返回它的值。如果指针是零,就返回bool类型的数据成员的值。然后写5个cpp文件,每一个都包含Mirror头文件。第一个cpp文件通过使用默认构造函数定义一个全局的Mirror对象。第二个cpp文件把第一个文件中定义的对象声明为extern,并通过使用第二个构造函数定义一个全局的Mirror对象,用一个指针指向第一个对象,在第三、四、五个文件中也做同样的处理。在最后一个文件中当然也包括一个全局对象的定义,并且main()应该调用test_()函数并报告结果。如果结果为true,找出该如何改变连接器的连接顺序来使返回的结果为false。
10-33 用本书中介绍的技术一修改练习32中的程序。
10-34 用本书中介绍的技术二修改练习32中的程序。
10-35 写一个不包含任何头文件的程序,用标准的C库函数声明puts(),在主函数main()中调用这个函数。