#include <iostream>
#include <string>
using namespace std;
class Screen
{
public:
typedef std::string::size_type pos;
// using pos=std::string::size_type; 同上等价
Screen() = default; //因为Screen有另一个构造函数,
//所以本函数是必须的,默认方式
Screen(pos ht, pos wd, char c) :height(ht), width(wd),
contents(ht*wd, c){} //contents初始化的意思是,有ht乘wd个字符c初始化一个string类型
char get() const //读取光标处的字符
{
return contents[cursor]; //隐式内联
}
inline char get(pos ht, pos wd) const; //显式内联
Screen &move(pos r, pos c); //能在之后被设为内联
private:
pos cursor = 0;
pos height = 0, width = 0;
std::string contents;
};
//虽然我们没必要在类的里面和外面都用inline来声明,但是这样
//是合法的,不过最好只在类的外部说明inline,这样更容易理解
inline //可以在函数的定义处指定
Screen &Screen::move(pos r, pos c)
{
pos row = r*width; //计算行的位置
cursor = row + c; //在行内将光标移到到指定的列
return *this; //以左值的形式返回对象
}
char Screen::get(pos r, pos c) const //在类的内部声明成inline
{
pos row = r*width; //计算行的位置
return contents[row + c]; //返回给定列的字符
}
int main()
{
Screen p(10, 10,'a');
char k = p.get(5,7);
cout << k << endl;
return 0;
}
内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质。内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看编译器对该函数定义的具体处理
内联说明(inline specification)对于编译器来说只是一个建议,编译器可以选择忽略这个建议
char k = p.get(5,7);
00CD6435 push 7
00CD6437 push 5
00CD6439 lea ecx,[ebp-3Ch]
//采用的调用的方式来调用内联函数,所以说明内联函数没有嵌入到调用处
00CD643C call 00CD14D8
00CD6441 mov byte ptr [ebp-45h],al
可变数据成员
使用关键字mutable,这样一个const成员函数也可以改变一个可变成员的值了
类内初始值就是把类内部的数据进行默认初始化,要么放在花括号里面,要么放在=号右边,不能使用圆括号
class Screen
{
public:
typedef std::string::size_type pos;
// using pos=std::string::size_type; 同上等价
Screen()=default; //因为Screen有另一个构造函数,
//所以本函数是必须的,默认方式
Screen(pos ht, pos wd, char c):height(ht), width(wd),
contents(ht*wd,c){} //contents初始化的意思是,有ht乘wd个字符c初始化一个string类型
char get() const //读取光标处的字符
{
return contents[cursor]; //隐式内联
}
inline char get(pos ht, pos wd) const; //显式内联
Screen &move(pos r, pos c); //能在之后被设为内联
void some_member() const;
private:
pos cursor=0;
pos height=0, width=0;
std::string contents;
mutable size_t access_ctr; //即使在一个const对象内也能被修改
};
//虽然我们没必要在类的里面和外面都用inline来声明,但是这样
//是合法的,不过最好只在类的外部说明inline,这样更容易理解
inline //可以在函数的定义处指定
Screen &Screen::move(pos r, pos c)
{
pos row=r*width; //计算行的位置
cursor=row+c; //在行内将光标移到到指定的列
return *this; //以左值的形式返回对象
}
char Screen::get(pos r, pos c) const //在类的内部声明成inline
{
pos row=r*width; //计算行的位置
return contents[row+c]; //返回给定列的字符
}
void Screen::some_member() const
{
++access_ctr; //保管一个计数值,用于记录成员函数被调用的次数
}
class Window_mgr
{
private:
//这个Window_mgr追踪的Screen
//默认情况下,一个Window_mgr包含一个标准尺寸的空白Screen
std::vector<Screen> screens{Screen(24, 80, ' ')};
};
返回*this的成员函数
返回*this的成员函数,则调用的直接就是类的对象本身,而不是他的副本。
#include <iostream>
#include <string>
using namespace std;
class Screen
{
public:
typedef std::string::size_type index;
Screen(index ht = 0, index wd = 0) :contents(ht*wd, 'A'), cursor(0), height(ht), width(wd)
{}
char get() const{ return contents[cursor]; }
char get(index r, index c)const
{
index row = r*width;
return contents[row + c];
}
Screen& move(index r, index c);
Screen& set(index, index, char);
Screen& set(char);
private:
std::string contents;
index cursor;
index height, width;
};
Screen& Screen::move(index r, index c)
{
index row = r*width;
cursor = row + c;
return *this;
}
Screen& Screen::set(index r, index c, char ch)
{
index row = r*width;
contents[row + c] = ch;
return *this;
}
Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
int main()
{
Screen myScreen(5, 5);
myScreen.move(4, 0).set('#');
cout << myScreen.get() << endl;
}
从const成员函数返回*this
在普通的非const成员函数中,this的类型是一个指向类类型的const指针,可以改变this所指向的值,但不能改变this所保存的地址。在const成员函数中,this的类型是一个指向const类类型的const指针,既不能改变this所指向的对象,也不能改变this所保存的地址
不能从const所员函数返回指向类对象的普通引用。const成员函数只能返回*this作为一个const 引用。
基于const的重载:
#include <iostream>
#include <string>
using namespace std;
class Screen
{
public:
typedef std::string::size_type index;
Screen(index ht = 0, index wd = 0) :contents(ht*wd, 'A'), cursor(0), height(ht), width(wd)
{}
char get() const{ return contents[cursor]; }
char get(index r, index c)const
{
index row = r*width;
return contents[row + c];
}
Screen& move(index r, index c);
Screen& set(index, index, char);
Screen& set(char);
const Screen& display(std::ostream &os)const
{
do_display(os);
return *this;
}
Screen& display(std::ostream &os)
{
do_display(os);
return *this;
}
private:
std::string contents;
index cursor;
index height, width;
void do_display(std::ostream &os)const
{
os << contents;
}
};
Screen& Screen::move(index r, index c)
{
index row = r*width;
cursor = row + c;
return *this;
}
Screen& Screen::set(index r, index c, char ch)
{
index row = r*width;
contents[row + c] = ch;
return *this;
}
Screen& Screen::set(char c)
{
contents[cursor] = c;
return *this;
}
int main()
{
Screen myScreen(3, 3);
myScreen.display(cout).set('#').display(cout);
cout << endl;
const Screen blank(3, 3);
myScreen.set('#').display(cout);//调用非常量版本
cout << endl;
blank.display(cout);//调用常量版本
return 0;
}
书本P144说:允许指向非常量类型指针转换成指向相应的常量类型指针,对于引用也是这样。也就是说,如果T是一种类型,我们就能将指向T的指针或引用分别装换成指向const T的指针或类型。
一个成员调用另一个成员时,this指针在其中隐式地传递。因此,当display调用do_display时,它的this指针将隐式地从指向非常量的指针转换成指向常量的指针。当do_display完成后,display函数各自解引用this所得的对象。在非常量版本中,this指向一个非常量对象,因此display返回一个普通的(非常量)引用;而const成员则返回一个常量引用。