类的其他特性(2)
返回*this的成员函数
返回*this的成员函数,则调用的直接就是类的对象本身,而不是他的副本。
#include<iostream>
#include<vector>
using namespace std;
class Screen
{
public:
typedef std::string::size_type pos; //用pos来代表这个类型
// using pos=std::string::size_type; 同上等价
Screen &set(char);
Screen &set(pos, pos, char);
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 Screen &Screen::set(char c)
{
contents[cursor]=c; //设置当前光标所在位置的新值
return *this; //将this对象作为左值返回
}
inline Screen &Screen::set(pos r, pos col, char ch)
{
contents[r*width+col]=ch; //设置给定位置的新值
return *this; //将this对象作为左值返回
}
//虽然我们没必要在类的里面和外面都用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, ' ')};
};
class Sales_data
{
public:
void avg_price();
};
inline
void Sales_data::avg_price()
{
}
int main()
{
Screen myScreen;
myScreen.move(4,0).set('#');
//上面等价于
//myScreen.move(4,0);
//myScreen.set('#');
}
从const成员函数返回*this
我们继续添加一个操作,叫display,负责打印Screen的内容,返回的是const类型的。
但是这样的话,返回后就无法进行操作了,因为返回的类型是const的类型。
基于const的重载
由于非常量版本的函数对于常量对象是不可用的,所以我们只能在一个常量对象上调用const成员函数
这里我们就定义一个名为do_display的私有成员,由它负责打印Screen的实际工作。所有的display操作都将调用这个函数
Screen &display(std::ostream &os)
{
do_diaplay(os);
return *this;
}
const Screen &display(std::ostream &os) const
{
do_diaplay(os);
return *this;
}
私有成员是:
void do_diaplay(std::ostream &os) const
{
os<<contents;
}
为什么要这样写呢?
因为当一个成员调用另外一个成员时,this指针在其中隐式地传递。因此,当display调用do_diaplay时,它的this指针隐式地传递给do_diaplay。
而当display非常量版本调用do_diaplay的时候,它的this指针将隐式地从指向非常量的指针转换成指向常量的指针。
当do_diaplay完成后,display函数各自返回解引用this所得的对象。
Screen myScreen(5,3,'c');
const Screen blank(5,3,'b');
myScreen.move(4,0).set('#');
//上面等价于
//myScreen.move(4,0);
//myScreen.set('#');
myScreen.set('#').display(cout); //调用非常量版本
blank.display(cout); //调用非常量版本
类类型
即使两个类的成员一模一样,他们也是两个不同的类型
习题:7.31
class X;
class Y
{
X *p;
};
class X
{
Y *p;
};
友元再探
类之间的友元关系
//Window_mgr的成员可以访问Screen类的私有部分
friend class Window_mgr;
class Window_mgr
{
private:
//这个Window_mgr追踪的Screen
//默认情况下,一个Window_mgr包含一个标准尺寸的空白Screen
std::vector<Screen> screens{Screen(24, 80, ' ')};
public:
//窗口中每个屏幕的编号
using ScreenIndex=std::vector<Screen>::size_type;
//按编号将指定的Screen重置为空白
void clear(ScreenIndex);
};
void Window_mgr::clear(ScreenIndex i)
{
//s是一个Screen的引用,指向我们想清空的那个屏幕
Screen &s=screens[i];
//将那个选定的Screen重置为空白
s.contents=string(s.height*s.width, '');
}
函数重载和友元
extern std::ostream& storeOn(std::ostream &, Screen &);
extern BitMap& storeOn(BitMap &, Screen &);
class Screen里面
friend std::ostream& storeOn(std::ostream &, Screen &);
全部代码实现:
/*
* 功能:类的前向声明,和友元,函数声明
* 时间:2014年6月2日16:38:51
* 作者:cutter_point
*/
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class Window_mgr;
class Screen
{
public:
typedef std::string::size_type pos; //用pos来代表这个类型
// using pos=std::string::size_type; 同上等价
Screen &set(char);
Screen &set(pos, pos, char);
Screen &move(pos r, pos c); //能在之后被设为内联
// cosnt Screen &display() const;
Screen &display(std::ostream &os)
{
do_diaplay(os);
return *this;
}
const Screen &display(std::ostream &os) const
{
do_diaplay(os);
return *this;
}
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; //显式内联
void some_member() const;
//Window_mgr的成员可以访问Screen类的私有部分
friend class Window_mgr;
using ScreenIndex=std::vector<Screen>::size_type;
//注意这里是函数声明不要在函数前面加Window_mgr::clear不然这事错的,你还要定义在
//类Window_mgr里面,在前面定义的话后面的私有成员又不好搞了!!!
friend void clear(ScreenIndex);
friend std::ostream& storeOn(std::ostream &, Screen &);
private:
pos cursor=0;
pos height=0, width=0;
std::string contents;
mutable size_t access_ctr; //即使在一个const对象内也能被修改
//该函数负责显示Screen的内容
void do_diaplay(std::ostream &os) const
{
os<<contents;
}
};
/*
Screen &Screen::display()
{
return *this;
}
*/
inline Screen &Screen::set(char c)
{
contents[cursor]=c; //设置当前光标所在位置的新值
return *this; //将this对象作为左值返回
}
inline Screen &Screen::set(pos r, pos col, char ch)
{
contents[r*width+col]=ch; //设置给定位置的新值
return *this; //将this对象作为左值返回
}
//虽然我们没必要在类的里面和外面都用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
{
public:
//窗口中每个屏幕的编号
using ScreenIndex=std::vector<Screen>::size_type;
//按编号将指定的Screen重置为空白
void clear(ScreenIndex);
private:
//这个Window_mgr追踪的Screen
//默认情况下,一个Window_mgr包含一个标准尺寸的空白Screen
std::vector<Screen> screens{Screen(24, 80, ' ')};
};
void Window_mgr::clear(ScreenIndex i)
{
//s是一个Screen的引用,指向我们想清空的那个屏幕
Screen &s=screens[i];
//将那个选定的Screen重置为空白
s.contents=string(s.height*s.width, ' ');
}
class Sales_data
{
public:
void avg_price();
};
inline
void Sales_data::avg_price()
{
}
int main()
{
// Screen myScreen;
Screen myScreen(5,3,'c');
const Screen blank(5,3,'b');
myScreen.move(4,0).set('#');
//上面等价于
//myScreen.move(4,0);
//myScreen.set('#');
myScreen.set('#').display(cout); //调用非常量版本
blank.display(cout); //调用非常量版本
return 0;
}
/************************************************************************/
/*
class X;
class Y
{
X *p;
};
class X
{
Y *p;
};
*/
class BitMap;
extern std::ostream& storeOn(std::ostream &, Screen &);
extern BitMap& storeOn(BitMap &, Screen &);