C++primer18章课后答案
18.1
(a)的异常对象类型为range_error
(b)的异常对象类型为exception
因为异常类型的对象类型是由表达式在编译时的静态类型决定的。
如果写成了throw p,在抛出异常对象p时调用终止
18.2
vector会调用自己的析构函数销毁自身
动态分配的内存空间将不会被释放
in内存储的对象被销毁
18.3
第一种方法是使用一个类管理动态数组
template<typename T>
struct DynamicArry
{
explicit DynamicArry(size_t n) :ptr(new T[n]) {}
~DynamicArry() { delete []ptr; }
T* ptr;
};
第二种方法是使用智能指针管理
std::shared_ptr<int> p(new int[v.size()], [](int* p) {delete[] p; })
18.4
顺序错误,应该把继承链最底端的类放在前面。
18.5
int main()
{
try {
//使用C++标准库
}
catch(...)
{
//处理异常
}
}
18.6
#include <iostream>
#include<cstdlib>
int main()
{
try {
//使用C++标准库
}
catch(std::exception&e)
{
std:: cout << e.what() << std::endl;
abort();
}
}
18.6
(a)throw new exceptionType();
(b)throw exception();
(c)throw 1;
18.7
StrBlob::StrBlob()try :data(make_shared<vector<string>>()) {}
catch (...) { cout << "StrBlob constructor error" << endl; };
18.8
略
18.9
Sales_data& Sales_data::operator +=(const Sales_data& rhs)
{
if (isbn() != rhs.isbn())
throw isbn_mismatch("wrong isbn", isbn(), rhs.isbn());
this->revenue += rhs.revenue;
this->units_sold += rhs.units_sold;
return *this;
}
class out_of_stock :public std::runtime_error
{
public:
explicit out_of_stock(const std::string& msg) :std::runtime_error(msg) {}
};
class isbn_mismatch :public std::logic_error
{
public:
explicit isbn_mismatch(const std::string& s, const std::string& l, const std::string& r) :std::logic_error(s), left(l), right(r) {}
const std::string left, right;
};
18.10
未被捕获的异常会导致程序异常退出。
int main()
{
Sales_data s1("0123");
Sales_data s2("3210");
try { s1 += s2; }
catch (isbn_mismatch& e)
{
cerr << e.what() << " :left isbn(" << e.left << ") right isbn(" << e.right << ")" << endl;
}
}
18.11
what函数是用来处理异常信息的,如果what函数会抛出异常,会造成无限递归。
18.12
略
18.13
希望定义的类类型,对象,函数,或其他实体只在当前一个文件可见时使用,在C++11中static声明已经被取消了,使用未命名空间代替。
18.14
mathLib::MatrixLib::matrix mathLib::MatrixLib::operator*(matrix &a,matrix &b);
18.15
- using声明:一次只能引入命名空间的一个成员,其有效范围从using声明开始,到using声明所在的作用域结束为止
- using指示:与using声明类似,using namespace XX,将XX命名空间的所有成员变成可见,作用域和using声明一致。using指示一般被看作是出现在最近的外层作用域中,若有同名冲突成员,应加限定符予以区分
18.16
namespace Exercise
{
int ivar = 0;
double dvar = 0;
const int limit = 1000;
}
int ivar = 0;
//1.1
//using Exercise::ivar;//错误,重定义,多次初始化
//using Exercise::dvar;
//using Exercise::limit;
//void manip()
//{
// double dvar = 3.1416;//覆盖using声明的dvar
// int iobj = limit + 1;
// ++ivar;
// ++::ivar;
//}
//1.2
//void manip()
//{
// using Exercise::ivar;//隐藏全局变量
// using Exercise::dvar;
// using Exercise::limit;
// double dvar = 3.1416;//错误,重定义,多次初始化
// int iobj = limit + 1;
// ++ivar;//Exercise的ivar
// ++::ivar;//全局变量
//}
//2.1
//using namespace Exercise;
//void manip()
//{
// double dvar = 3.1416;//覆盖using声明的dvar
// int iobj = limit + 1;
// ++ivar;//错误,不明确,二义性,可能是int ivar或int Exercise::ivar
// ++::ivar;
//}
//2.2
//void manip()
//{
// using namespace Exercise;
// double dvar = 3.1416;//覆盖using声明的dvar
// int iobj = limit + 1;
// ++ivar;//错误,不明确,二义性,二者都可见
// ++::ivar;
//}
int main()
{
manip();
}
18.17
见上题
18.18
当使用string时,在string类中查找,如果查找不到,则在std命名空间中查找。使用int时在标准库中查找,使用标准库中的swap
18.19
直接使用标准库版本的swap
18.20
来源
- using指示将命名空间的成员提升到外层作用域中,若两空间有同名函数,则会被添加到重载集合中,且引入一个与已有函数形参列表完全相同的函数不会造成错误,只要我们指明使用哪个版本的函数即可
- using声明引入的函数将重载该声明语句所属作用域中已有的同名函数,如果using声明出现在局部作用域中,则引入的名字会隐藏外层作用域中的同名声明
namespace p
{
void compute();//不可行
void compute(const void *);//可行,0->NULL
}
using p::compute;
void compute(int);//可行,最佳匹配
void compute(double, double = 1.1);//可行,int->double
void compute(char*, char* = 0);//可行,0->NULL
void f()
{
compute(0);
}
namespace p{
void compute();//不可行,可见
void compute(const void *);//可行,0->NULL,可见
}
void compute(int);//可行,不可见,被隐藏
void compute(double, double = 1.1);//可行,int->double,被隐藏
void compute(char*, char* = 0);//可行,0->NULL,被隐藏
void f(){
using p::compute;
compute(0);
}
18.21
- 缺少一个说明符
- 派生列表不能有同名基类
- 正确
18.22
执行顺序为A-B-C-X-Y-Z-MI
18.23
都允许,但是一个基类的指针只能访问基类部分和基类的基类部分。
18.24
ZooAnimal *pz=new Panda("ying_yang");
pz->print();//正确,使用Panda::print
pz->cuddle();//错误,不属于ZooAnimal接口
pz->toes();//错误,不属于ZooAnimal接口
pz->highlight();//错误,不属于ZooAnimal接口
delete pz;//正确,调用~Panda();
18.25
调用析构函数顺序与构造函数相反
(a)MI::print()
(b)MI::print()
( c)MI::print()
(d)~MI() ~D2() ~B2() ~D1() ~B1()
(e)~MI() ~D2() ~B2() ~D1() ~B1()
(f)~MI() ~D2() ~B2() ~D1() ~B1()
18.26
二义性,应该声明使用的是哪个类的print()函数,修改为mi.Base1::print()
18.27
(a)
- Base1:int ival,char cval,int *id,print;
- Base2: double fval,print
- Derived:string sval,print
- MI:int*ival,vector<double>dvec,print,foo
- 还有foo中定义的dval
(b)
ival,print,cval,dval
(c )
dval=Base1::dval+Derived::dval;
(d)
fval=dvec.back();
(e)
sval=Base1::cval;
18.28
可以直接访问的有Derived2::ival;void Derived1::bar(),其他需要加访问限定符
18.29
(a)构造函数顺序:Class,base,D1,D2,MI,Class
析构函数顺序相反
(b)一个base,两个Class
(c)
- 错误,Class类是base的父类
- 正确,Class类可以指向所有的子类
- 错误,Base类是MI类的父类
- 正确,D2是MI类的父类
18.30
> class Class {};
class Base :public Class {
protected:
int ival;
public:
Base() :ival(0), Class() {};
Base(const Base& b) = default;
Base(int a) :ival(a), Class() {}
};
class D1 :public virtual Base {
public:
D1() :Base() {}
D1(const D1& b) = default;
D1(int a) :Base(a) {}
};
class D2 :public virtual Base {
public:
D2() :Base() {}
D2(const D2& b) = default;
D2(int a) :Base(a) {}
};
class MI :public D1, public D2 {
public:
MI() {}
MI(const MI& m) :Base(m), D1(m), D2(m) {}
MI(int i) :Base(i), D1(i), D2(i) {}
};
class Final :public MI, public Class {
public:
Final() {}
Final(const Final& f) : Base(f), MI(f), Class() {}
Final(int i) : Base(i), Class() {}
};