一、编写自己的Screen类。
知识点1:类可以自定义某种类型在类中的别名———通过typedef和using;
知识点2:如果我们需要合成的默认构造函数,在空列表的构造函数后加上=default即可;
知识点3:类的成员也可以重载,参数列表数量或者类型上不同;
知识点4:在变量前加关键字mutable,变为可变数据成员,即使是在一个const的对象函数中也可以被修改;
知识点5:类内初始值,直接用=赋予的方式是C++11新特性,也就是VS2013以上的版本才支持;
知识点6:当某个数据成员在构造函数初始化列表中忽略,它将以默认构造函数的方式隐式初始化;
#ifndef Cccc//第一次包含本头文件时,#ifndef判断为真,预处理器将处理后面的内容直到#endif,此时的预处理变量Cccc已定义
#define Cccc//第二次包含本头文件时,#ifndef判断为假,预处理器将忽略后面的内容
#include <string>
class Screen {
public:
using pos = std::string::size_type;//std库中的string类的sizetype类型
Screen() = default;
Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c)
{
}
char get() const { return contents[cursor]; }
char get(pos r, pos c) const { return contents[r * width + c]; }
private:
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
#endif//只要简单的加上就好了,无视C++中的作用域规则,作用是防止头文件被重复包含
/*
c++ string::size_type详解
string::size_type类型
从逻辑上来讲,size()成员函数似乎应该返回整型数值,或如2.2节“建议”中所述的无符号整数。但事实上,size操作返回的是string::size_type类型的值。
我们需要对这种类型做一些解释。string类类型和许多其他库类型都定义了一些伙伴类型(companion types)。这些伙伴类型使得库类型的使用是机器无关的(machine-independent)。
size_type就是这些伙伴类型中的一种。它定义为与unsigned型(unsigned int或unsigned long)具有相同的含义,而且可以保证足够大可存储任意string对象的长度。
为了使用由string类型定义的size_type类型,程序员必须加上作用域操作符来说明所使用的size_type类型是由string类定义的。
任何存储string的size操作结果的变量必须为string::size_type类型。特别重要的是,不要把size的返回值赋给一个int变量。
虽然我们不知道string::size_type的确切类型,但可以知道它是unsigned型(2.1.1节)。对于任意一种给定的数据类型,它的unsigned型所能表示的最大正数值比对应的signed要大一倍。
这个事实表明size_type存储的string长度是int所能存储的两倍。
使用int变量的另一个问题是,有些机器上int变量的表示范围太小,甚至无法存储实际并不长的string对象。如在有16位int型的机器上,int类型变量最大只能表示32767个字符的string对象。
而能容纳一个文件内容的string对象轻易就会超过这个数字。因此,为了避免溢出,保存一个string对象size的最安全的方法就是使用标准库类型string:: size_type。
string类类型和许多其他库类型都定义了一些配套类型(companion type)。通过这些配套类型,库类型的使用就能和机器无关(machine-independent)。string::size_type定义为unsigned型,可以保证足够大的存储string对象的长度。
注意,任何存储string的size操作结果的变量必须为string::size_type类型。尤其不能把size的返回值赋给一个int变量。(因为size返回的是一个unsigned类型,而int是signed类型。size能表达的大小是int的2倍)。
string str("some string"); //通过字符串字面值赋值给串
for (string::size_type ix = 0; ix != str.size(); ++ix) //此处不该为int 用!=,而不用<=
{
cout<<str[ix]<<endl;
}
vector<int> ivec;
for(vector<int>::iterator ix = ivec.begin(); ix != ivec.end(); ++ix)
//此处不该为int 用!=,而不用<=
{
*ix = 0; //将各个元素赋值为0
}
综上所述:string::size_type 等价于 unsigned ,使用情况为存储字符串的大小
*/
二、给Screen类添加三个构造函数:一个默认构造函数;另一个构造函数接受宽和高的值,然后将contents初始化给定数量的空白;第三个构造函数接受宽和高的值以及一个字符,该字符作为初始化之后屏幕的内容。
Screen() = default; // 第一个构造函数
Screen(pos ht, pos wd) : height(ht), width(wd), contents(ht * wd, ' ') {} // 第二个构造函数
Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c)
{
} // 第三个构造函数
三、Screen能安全的依赖于拷贝和赋值操作的默认版本吗?如果能为什么?如果不能,为什么?
解析:只有内置类型和string类型可以依赖于操作的默认版本。
四、通过this指针使用成员的做法虽然合法,但是有点多余。讨论显示地使用指针访问成员的优缺点。
解析:优点:1:当需要将一个对象作为整体引用而不是引用对象的一个成员时,使用this,则该函数返回对调用该函数的对象的引用。
2:可以非常明确地指出访问的是调用该函数的对象的成员,且可以在成员函数中使用与数据成员同名的形参。
缺点:不必要使用,代码多余。
五、定义一对类X和Y,其中X包含一个指向Y的指针,而Y包含一个类型为X的对象。
知识点1:可以将类当作一种类型来看待,两个类即使成员部分完全相同,也属于不同的类型;
知识点2:类允许包含指向它自身类型的指针或者引用;
知识点3:创建一个类的对象之前必须先定义该类,仅仅声明是无法让编译器了解到该类需要多少空间的;
#ifndef Cccc//第一次包含本头文件时,#ifndef判断为真,预处理器将处理后面的内容直到#endif,此时的预处理变量Cccc已定义
#define Cccc//第二次包含本头文件时,#ifndef判断为假,预处理器将忽略后面的内容
class X;//事先声明
class Y
{
X object;
};
class X
{
Y *my_pointer = NULL;
};
#endif//只要简单的加上就好了,无视C++中的作用域规则,作用是防止头文件被重复包含
六、定义你自己的Screen和Window_mgr,其中clear是Window_mgr的成员,是Screen的友元。
解析: 知识点1:若想将一组重载函数声明为一个类的友元,那么必须逐个进行声明
知识点2:在类内声明的友元必须在类内或者类外进行定义之后才可以使用
此题在clear()函数之前加 friend 关键字即可