《C++ Primer》第18章 18.2节习题答案

本篇博客详细介绍了C++中命名空间的使用,包括命名空间的作用、未命名命名空间的使用场景、以及如何在不同命名空间间导入和导出成员。同时,探讨了在异常处理中what函数不应抛出异常的原因,以及在大型程序中如何有效组织和管理代码。此外,还通过一系列练习题解析了命名空间、using指示和using声明的差异和用法,以及在不同作用域下的名字查找规则。
摘要由CSDN通过智能技术生成

《C++ Primer》第18章 用于大型程序的工具

18.2节 命名空间 习题答案

练习18.11:为什么what函数不应该抛出异常?

【出题思路】

深入理解what函数的作用和在异常处理中所处的重要位置。

【解答】

what函数是在catch异常后用于提取异常基本信息的虚函数,what函数是确保不会抛出任何异常的。如果what函数抛出了异常,则会在新产生的异常中由于what函数继续产生异常,将会产生抛出异常的死循环。所以what函数必须确保不抛出异常。

练习18.12:将你为之前各章练习编写的程序放置在各自的命名空间中。也就是说,命名空间chapter15包含Query程序的代码,命名空间chapter10包含TextQuery的代码;使用这种结构重新编译Query代码示例。

【出题思路】

本题练习将不同程序放置在不同的命名空间中。

【解答】

将Query类以及Query_base类层次定义为命名空间chapter15的成员,并相应修改主函数中的代码(使用限定名引用这些类,或者使用相关的using声明)。

练习18.13:什么时候应该使用未命名的命名空间?

【出题思路】

理解未命名的命名空间的作用和意义。

【解答】

通常,当需要声明局部于文件的实体时,可以使用未命名的命名空间,即在文件的最外层作用域中定义未命名的命名空间。

练习18.14:假设下面的operator*声明是嵌套的命名空间mathLib::MatrixLib的一个成员:

namespace mathLib{
	namespace MatrixLib {
		class matrix { /* ... */ };
		matrix operator*(const matrix &, const matrix &);
		// ...
	}
}

请问你应该如何在全局作用域中声明该运算符?

【出题思路】

了解在嵌套的命名空间中如何声明全局的变量。

【解答】

将函数返回类型及函数名加上命名空间名字限定即可:

mathLib::MatrixLib::matrix mathLib::MatrixLib::operator*(const matrix &, const matrix &)
{ /* ... */ }

练习18.15:说明using指示与using声明的区别。

【出题思路】

深入了解using指示和using声明的特点和用处。

【解答】

一个using指示使得特定命名空间中的所有名字都成为可见的;而一个using声明只能引入特定命名空间中的一个成员。

练习18.16:假定在下面的代码中标记为“位置1”的地方是对于命名空间Exercise中所有成员的using声明,请解释代码的含义。如果这些using声明出现在“位置2”又会怎样呢?将using声明变为using指示,重新回答之前的问题。

namespace Exercise {
	int ivar = 0;
	double dvar = 0;
	const int limit = 1000;
}
int ivar = 0;
//位置1
void manip() {
	//位置2
	double dvar = 3.1416;
	int iobj = limit + 1;
	++ivar;
	++::ivar;
}

【出题思路】

理解命名空间的声明和指示在主函数内和主函数外的差别。

【解答】

如果命名空间Exercise的所有成员的using声明放在标记为“位置1”的地方,则Exercise中的成员在全局作用域中可见。using Exercise::ivar;会导致ivar重复定义的编译错误,因为在全局作用域中也定义了一个同名变量(注意,由using声明引起的二义性错误在声明点检测);而manip中的double dvar = 3.1416;声明了一个局部变量dvar,在函数体作用域中它将屏蔽Exercise::dvar; int iobj = limit + 1;声明了一个局部变量iobj,并用Exercise::limit加1的结果对其进行初始化。

如果命名空间Exercise的所有成员的using声明放在标记为“位置2”的地方,则manip中的double dvar = 3.1416;属于对变量dvar的重复定义,会出现编译错误;int iobj = limit + 1;声明了一个局部变量iobj,并用Exercise::limit加1的结果对其进行初始化;++ivar;访问到的是Exercise::ivar,而++::ivar访问的是全局变量ivar。

如果命名空间Exercise的using指示放在标记为“位置1”的地方,则manip中的double dvar = 3.1416;声明了一个局部变量dvar,在函数体作用域中它将屏蔽Exercise::dvar; int iobj = limit + 1;声明了一个局部变量iobj,并用Exercise::limit加1的结果对其进行初始化;++ivar;访问到的是Exercise::ivar,而++::ivar;访问的是全局变量ivar。

如果命名空间Exercise的using指示放在标记为“位置2”的地方,则Exercise的成员看来好像是声明在全局作用域中的一样,manip中的double dvar = 3.1416;声明了一个局部变量dvar,在函数体作用域中它将屏蔽Exercise::dvar;;int iobj =limit + 1;声明了一个局部变量iobj,并用Exercise::limit加1的结果对其进行初始化;++ivar;出现二义性错误,因为编译器无法分辨是访问Exercise::ivar,还是访问全局变量ivar;而++::ivar访问的是全局变量ivar。

练习18.17:实际编写代码检验你对上一题的回答是否正确。

【出题思路】

通过观察代码中相关变量的变化,深入了解命名空间的作用域。

【解答】

一共有4种情况,所以编程时需要编写4个对应的程序。程序一:

程序一:

#include <iostream>

using namespace std;

namespace Exercise
{
    int ivar = 10;
    double dvar = 0;
    const int limit = 1000;
}

int ivar = 20;
//位置1:插入using声明
//using Exercise::ivar;//编译错误ivar重复定义
using Exercise::dvar;
using Exercise::limit;

int main()
{
    //位置2
    double dvar = 3.1416;   //局部dvar
    int iobj = limit + 1;   //Exercise::limit;
    ++ivar;					//二义性
    ++::ivar;               //二义性
    cout << "dvar = " << dvar << endl;
    cout << "iobj = " << iobj << endl;
    cout << "ivar = " << ivar << endl;

    return 0;
}

运行结果:

 

程序二:

#include <iostream>

using namespace std;

namespace Exercise
{
    int ivar = 10;
    double dvar = 0;
    const int limit = 1000;
}

int ivar = 20;
//位置1:插入using声明


int main()
{
    //位置2:插入using声明
    using Exercise::ivar;
    using Exercise::dvar;
    using Exercise::limit;

    //double dvar = 3.1416;   //编译错误ivar重复定义
    int iobj = limit + 1;   //Exercise::limit;
    ++ivar;					//Exercise::ival;
    ++::ivar;               //二义性
    cout << "dvar = " << dvar << endl;
    cout << "iobj = " << iobj << endl;
    cout << "ivar = " << ivar << endl;

    return 0;
}

运行结果:

 

程序三:

#include <iostream>

using namespace std;

namespace Exercise
{
    int ivar = 10;
    double dvar = 0;
    const int limit = 1000;
}

int ivar = 20;
//位置1:插入using声明
using namespace Exercise;

int main()
{
    //位置2

    double dvar = 3.1416;   //Exercise::dvar;
    int iobj = limit + 1;   //Exercise::limit;
    //++ivar;					//二义性
    ++::ivar;               //二义性
    cout << "dvar = " << dvar << endl;
    cout << "iobj = " << iobj << endl;
    cout << "ivar = " << ::ivar << endl;

    return 0;
}

运行结果:

 

程序四:

#include <iostream>

using namespace std;

namespace Exercise
{
    int ivar = 10;
    double dvar = 0;
    const int limit = 1000;
}

int ivar = 20;
//位置1

int main()
{
    //位置2:插入using声明
    using namespace Exercise;

    double dvar = 3.1416;   //Exercise::dvar;
    int iobj = limit + 1;   //Exercise::limit;
    //++ivar;					//二义性
    ++::ivar;               //二义性
    cout << "dvar = " << dvar << endl;
    cout << "iobj = " << iobj << endl;
    cout << "ivar = " << ::ivar << endl;

    return 0;
}

运行结果:

 

练习18.18:已知有下面的swap的典型定义(参与13.3节,第457页),当mem1是一个string时程序使用swap的哪个版本?如果mem1是int呢?说明在这两种情况下名字查找的过程。

void swap(T v1, T v2)
{
	using std::swap;
	swap(v1.mem1, v2.mem1);
	//交换类型T的其他成员
}

【出题思路】

深入理解同一函数在拥有不同类型的参数时名字查找的过程。

【解答】

如果mem1是string类型,编译器除了在常规作用域中查找匹配的swap外,还会查找string所属的命名空间中是否有string类型特定版本的swap函数。但对string而言,找到的就是std::swap,完成两个字符串内容的交换。若mem1是int类型,由于int是内置类型,没有特定版本的swap,只会在常规作用域中查找。由于using声明的作用,最终会调用std::swap,完成两个int的交换。

练习18.19:如果对swap的调用形如std::swap(v1.mem1, v2.mem1)将发生什么情况?

【出题思路】

理解强制调用特定版本的函数时需要如何操作。

【解答】

将直接使用标准库版本的swap,而不会查找特定版本的swap或常规作用域中的其他swap。

练习18.20:在下面的代码中,确定哪个函数与compute调用匹配。列出所有候选函数和可行函数,对于每个可行函数的实参与形参的匹配过程来说,发生了哪种类型转换?

namespace primerLib{
	void compute();
	void compute(const void *);
}
using primerLib::compute;
void compute(int);
void compute(double, double = 3.4;
void compute(char *, char * = 0);
void f()
{
	compute(0);
}

如果将using声明置于函数f中compute的调用点之前将发生什么情况?重新回答之前的那些问题。【出题思路】

理解主函数内外using声明和重载的特性。

【解答】

全局作用域中声明的函数void compute(int)与compute函数的调用匹配。

候选函数:命名空间primerLib中声明的两个compute函数(因using声明使得它们在全局作用局中可见),以及全局作用域中声明的三个compute函数。

可行函数:因函数调用中给出的实参0为int型,所以可行函数为以下4个函数:

void compute(int);
void compute(double, double = 3.4;
void compute(char *, char * = 0);
primerLib中声明的void compute(const void*)

其中,第一个为完全匹配,第二个需要将实参隐式转换为double类型,第三个需要将实参隐式转换为char*类型,第四个需要将实参隐式转换为void*类型方可匹配,所以第一个为最佳匹配。

如果将using声明置于函数f中compute的调用点之前,则primerLib中声明的voidcompute(const void*)与compute函数的调用匹配。

候选函数:命名空间primerLib中声明的两个compute函数(因using声明使得它们在函数f的函数体作用域中可见,并屏蔽了全局作用域中的三个compute函数)。

可行函数:因函数调用中给出的实参0为int型,所以可行函数为primerLib中声明的void compute(const void*)。需要将实参隐式转换为void*类型方可匹配。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值