《C++ Primer》第7章 类
7.4节 类的作用域
练习7.33:如果我们给Screen添加一个如下所示的size成员将会发生什么情况?发果出现了问题,请尝试修改它。
pos Screen::size() const
{
return height * width;
}
【出题思路】
考查类的作用域。
【解答】
如果添加如题目所示的size函数将会出现编译错误。因为该函数的返回类型pos本身定义在Screen类的内部,所以在类的外部无法直接使用pos。要想使用pos,需要在它的前面加上作用域Screen::。修改后的程序是:
Screen::pos Screen::size() const
{
return height * width;
}
#include <iostream>
#include <string>
using namespace std;
class Window_mgr
{
public:
void clear();
};
class Screen
{
friend void Window_mgr::clear(); //不定义友元,编译通不过
public:
typedef std::string::size_type pos;
Screen() = default;
Screen(unsigned ht, unsigned wd, char c)
:height(ht), width(wd), contents(ht * wd, c)
{
}
pos size() const;
private:
unsigned height = 0, width = 0;
unsigned cursor = 0;
string contents;
pos ht = 24, wd = 80;
};
Screen::pos Screen::size() const //pos前面要加作用域
{
return height * width;
}
void Window_mgr::clear()
{
Screen myScreen(10, 20, 'X');
cout << "请理之前myScreen的内容是:" << endl;
cout << myScreen.contents << endl;
cout << "size=======" << myScreen.size() << endl;
myScreen.contents = "";
cout << "请理之后myScreen的内容是:" << endl;
cout << myScreen.contents << endl;
}
int main()
{
Window_mgr w;
w.clear();
return 0;
}
运行结果:
练习7.34:如果我们把第256页Screen类的pos的typedef放在类的最后一行会发生什么情况?
【出题思路】
本题考查用于类成员声明的名字查找。
【本题解答】
这样做会导致编译出错,因为对pos的使用出现在它的声明之前,此时编译器并不知道pos到底是什么含义。
注意:类的编译分为两部先编译类的成员,再编译函数体。
练习35:解释下面代码的含义,说明期中的和分别使用了哪个定义,如果代码存在错误,尝试修改它。
typedef string Type;
Type initVal();
class Exercise {
public:
typedef double Type;
Type setVal(Type);
Type initVal();
private:
int val;
};
Type Exercise::setVal(Type parm) {
val = parm + initVal();
return val;
}
【出题思路】
理解名字查找与类的作用域的关系,包括用于类成员声明的名字查找和成员定义中的名字查找。
【解答】
代码的含义及Type和initVal的使用请参考注释。
typedef string Type; //声明类型别名Type表示string
Type initVal(); //声明函数initVal,返回类型是Type
class Exercise //定义一个新类Exercise
{
public:
typedef double Type; //在内层作用域重新声明类型别名Type表示double
Type setVal(Type); //声明函灵敏setVal,参数和返回值的类型都是Type
Type initVal(); //在内层作用域重新声明函数initVal,返回类型是Type
private:
int val; //声明私有数据成员val
};
//定义函数setVal,此时的Type显然是外层作用域string
Type Exercise::setVal(Type parm)
{
val = parm + initVal(); //此处使用的是类内的initVal函数
return val; //返回的是double
}
//改成
Exercise::Type Exercise::setVal(Type parm)
{
val = parm + initVal(); //此处使用的是类内的initVal函数
return val; //返回的是double
}
其中,在Exercise类的内部,函数setVal和initVal用到的Type都是Exercise内部声明的类型别名,对应的实际类型是double。在Exercise类的外部,定义Exercise::setVal函数时形参类型Type用的是Exercise内部定义的别名,对应double;返回类型Type用的是全局作用域的别名,对应string。使用的initVal函数是Exercise类内定义的版本。编译上述程序时在setVal的定义处发生错误,此处定义的函数形参类型是double、返回值类型是string,而类内声明的同名函数形参类型是double、返回值类型也是double,二者无法匹配。修改的措施是在定义setVal函数时使用作用域运算符强制指定函数的返回值类型。