类的其他特性

#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成员则返回一个常量引用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值