题目的要求就是扩展书上的Screen 类,使之包含move,set,display操作,以这个例题来进行一些类操作的学习
</pre><pre name="code" class="cpp">//将光标移至指定位置,设置字符并在屏幕显示
myScreen.move(4,0) .set('#'). display(cout);
<span style="font-family: Arial, Helvetica, sans-serif;"></span>
首先从main开始(不知道这样子思考是不是有问题。。。)
应该具有哪些功能,光标要移动~要能改变~要显示~~~
int main()
{
Screen myScreem(5, 6, "aaaaa\naaaaa\naaaaa\naaaaa\naaaaaa\n");
myScreem.move(4,0). set('#'). display(cout);
return 0;
}
其中:</pre><pre name="code" class="cpp">myScreem.move(4,0). set('#'). display(cout);
//其等价于
myScreen.move(4,0);
myScreen.set('#');
myScreen.(cout);
理想情况下希望用户能够将这些操作的序列连接成一个单独的表达式。
且进行了move操作后,应该返回Screen的引用,再去操作set...
类 Screen:
</pre><pre name="code" class="cpp">class Screen {
public:
typedef string::size_type index;
char get() const { return contents[cursor]; }
inline char get(index ht , index wd) const ;
index get_cursor() const;
Screen(index hght , index wdth, const string &cntnts);
//add三个成员函数的定义
Screen& move(index r , index c);
Screen& set(char c);
Screen& display(ostream &os);
private:
string contents;
index cursor;
index height, width;
};
定义一个名为 Screen 的类型表示计算机上的窗口。每个 Screen 可以有一个保存窗口内容的 string 成员,以及三个 string::size_type 成员:一个指定光标当前停留的字符,另外两个指定窗口的高度和宽度。
① 为 std::string::size_type 提供一个类型别名,使得类更加抽象,将 index 的定义放在类的 public 部分,是因为希望用户使用这个名字。Screen 类的使用者不必了解用 string 实现的底层细节。
② Screen 类的两个重载成员,一个版本返回由当前光标指示的字符,另一个返回指定行列处的字符。给指定的函数调用提供适当数目和/或类型的实参来选择运行哪个版本。这是为什么呢??
③ 将关键字const加在形参表后,就可以将成员函数声明为常量,const成员不能改变其所操作的对象的数据成员。const必须同时出现在声明和定义中,否则会出现编译器报错。
Screen::Screen(index hght , index wdth, const string &cntnts) :
contents(cntnts), cursor(0), height(hght), width(wdth) // initialize
{
}
char Screen::get(index r , index c) const
{
index row = r * width;
return contents[row * c];
}
inline Screen::index Screen::get_cursor() const
{
return cursor;
}
① 构造函数初始化列表,在函数形参表之后,以冒号“ : ”开头,由成员名和带括号的初始值组成。
②
//三个成员函数的定义
Screen& Screen::move(index r , index c)
{
index row = r * width;
cursor = row + c;
return *this;
}
Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
Screen& Screen::display(ostream &os)
{
os << contents;
return *this;
}
this指针:每个函数都返回调用自己的那个对象。使用this指针来访问该对象。在以上这3个函数中,this 是一个指向非常量 Screen 的指针。如同任意的指针一样,可以通过对 this 指针解引用来访问 this 指向的对象。
从const成员函数返回*this。
5*6是一种矩阵形式
再进一步思考如果是任何字符形式的文本如何处理。
上述解决方法已经满足了题目提出的要求,但是仍存在一些缺陷:
① 创建Screen对象时必须给出表示整个屏幕内容的字符串,即使有些位置没有内容。
修改如下:
Screen::Screen(index hght , index wdth, const string& cntnts = " ") :
contents(cntnts), cursor(0), height(hght), width(wdth)// initialize
{
//将整个屏幕内容置为空格
contents.assign(hght*wdth, ' ');
//用形参string对象的内容设置屏幕相应字符
if (cntnts.size() != 0)
{
contents.replace(0 , cntnts.size() , cntnts);
}
}
② 显示的屏幕内容没有恰当地分行,而是连续显示,因此(4,0)位置上的'#'在实际显示时,不一定正好在屏幕(4,0)位置,显示效果较差。
修改如下:
Screen& Screen::display(ostream &os)
{
string::size_type index = 0;
while(index != contents.size())
{
os << contents[index];
if (( index + 1 ) % width ==0)
{//位于某行结尾
os << '\n';
}
++index;
}
return *this;
}
③ 如果创建的Screen对象是一个const对象,则不能使用display函数进行显示。
可以在Screen类定义中声明display函数的一个重载版本,供const对象使用,并在Screen类定义体外增加该函数的定义:
const Screen& display(ostream &os) const;
const Screen& Screen::display(ostream &os) const
{
string::size_type index = 0;
while (index != contents.size())
{
os << contents[index];
if (( index+1 ) % width ==0)
{
os << '\n';
}
++index;
}
return *this;
}
两个重载的display函数完全一样,因此为了减小代码重复,可以定义一个do_display函数来完成实际的显示工作,而两个重载的display函数都调用该函数。这个起辅助作用的do_display可以定义在Screen类的private部分。
类中在函数声明的后面添加const 表示函数操作不会改变对象的内容, 在非const成员函数中, this指针的类型是一个指向类类型的const 指针,(可以改变指向this指针 所指向的内容的值,不可以改变this所保存的值。而在const 成员函数中,this的类型是指向一个const 类类型对象的const 指针,即不能改变this所指向的对象,也不能改变this 所保存的地址。
不能从const 成员函数中返回类对象的普通引用,const 成员函数只能返回*this作为一个const 引用。
意思是:
class X
{
X& get() const { return *this; } // 错误!
const X& get() const { return *this; } // 正确
}
④ 如果move操作的目的位置超出了屏幕的边界,会出现运行时错误。
修改如下:
Screen& Screen::move(index r , index c)
{
if ( r >=height || c >=width)
{
cerr <<"invalid row or column" <<endl;
throw EXIT_FAILURE;
}
index row = r * width;
cursor = row + c;
return *this;
}