【c++ primer, 5e】类的其他特性

【Class Members Revisited】

1、Defining a Type Member:

#include <iostream>
#include <string>

using namespace std;
class Screen {
public:
    using pos = string::size_type;
    /*
        这就是“类型成员”,必须先定义后使用(p232)
        等价声明: typedef string::size_type pos;
        string::size_type 一般是 unsigned int
    */
private:
    pos cursor = 0; // 光标的位置
    pos height = 0, width = 0; // 屏幕的高和宽
    string contents; // 保存内容
};

int main()
{
    return 0;
}

2、Member Functions of class Screen。

class Screen {
public:
    using pos = string::size_type;
    
    Screen() = default;
    Screen(pos ht, pos wd, char c): height(ht), width(wd), contents(ht * wd, c) {}
    
    char get() const {
        return contents[cursor];
    } // implicitly inline
    inline char get(pos ht, pos wd) const; // explicitly inline
    Screen &move(pos r, pos c);
private:
    pos cursor = 0;
    pos height = 0, width = 0;
    string contents;
};

3、Making Members inline

 规模小的成员函数适合被声明为inline,例如上面的构造器和其中一个get函数默认是内联的。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 {
    pos row = r * width;
    return contents[row+c];
}

需要注意的是内联函数应当和相应的类定义在同一个头文件中。

4、Overloading Member Functions

    Screen myscreen;
    char ch = myscreen.get(); // calls Screen::get()
    ch = myscreen.get(0,0); // calls Screen::get(pos,pos)

5、mutable Data Members

极少的一种情况(例如记录const函数被调用的次数),我们希望在const成员函数中修改类的数据成员。(正常情况下是做不到的,const函数不能修改数据成员)这个时候,可以将变量声明成mutable来做到这一点。

public:
    void some_member() const {
        ++access_ctr;
    }
private:
    size_t access_ctr;

这种情况无法通过编译:

prog1.cpp: In member function 'void Screen::some_member() const':
prog1.cpp:20:5: error: increment of member 'Screen::access_ctr' in read-only object
   ++access_ctr;

把access_cstr声明成mutable即可:

mutable size_t access_ctr;

6、Initializers for Data Members of Class Type

class Window_mgr {
private:
    vector<Screen> screens{Screen(24, 80, ' ')};
};

类内初始值必须是等号形式或者花括号形式。

7.23 & 7.24

#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
using namespace std;

class Screen {
public:
    using pos = string::size_type;
    // constructors
    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) {}
    // Member Functions
    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;
    string contents;
};

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 {
    pos row = r * width;
    return contents[row+c];
}

class Window_mgr {
private:
    vector<Screen> screens{Screen(24, 80, ' ')};
};

int main()
{
    Screen myscreen;
    char ch = myscreen.get(); // calls Screen::get()
    ch = myscreen.get(0,0); // calls Screen::get(pos,pos)
    return 0;
}

7.25

可以,Screen类并没有涉及动态内存分配,仅包含基本数据类型、string成员,拷贝、赋值、销毁的默认合成版本可以正常工作。

7.26

在原函数的类外定义加上关键字inline即可。 

【Functions That Return *this】

// if move returns Screen not Screen&
Screen temp = myScreen.move(4,0); // the return value would be copied
temp.set('#'); // the contents inside myScreen would be unchanged

if move returns Screen&:

// move the cursor to a given position, and set that character
myScreen.move(4,0).set('#');

1、Returning *this from a const Member Function

 A const member function that returns *this as a reference should have a
return type that is a reference to const

2、Overloading Based on const 

class Screen {
public:
// display overloaded on whether the object is const or not
Screen &display(std::ostream &os)
{ do_display(os); return *this; }
const Screen &display(std::ostream &os) const
{ do_display(os); return *this; }
private:
// function to do the work of displaying a Screen
void do_display(std::ostream &os) const {os <<
contents;}
// other members as before
};

When we call display on an object, whether that object is const determines which version of display is called:

Screen myScreen(5,3);
const Screen blank(5, 3);
myScreen.set('#').display(cout); // calls non const version
blank.display(cout); // calls const version

7.27

#include <cstdio>
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
using namespace std;

class Screen {
public:
    using pos = string::size_type;
    // constructors
    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) {}
    // Member Functions
    char get() const { return contents[cursor]; }
    inline char get(pos ht, pos wd) const;
    
    Screen &move(pos r, pos c);
    
    Screen &set(char);
    Screen &set(pos, pos, char);
    
    Screen &display(ostream &os) {
        do_display(os); return *this;
    }
    const Screen &display(ostream &os) const {
        do_display(os); return *this;
    }
private:
    pos cursor = 0;
    pos height = 0, width = 0;
    string contents;
    
    void do_display(std::ostream &os) const { os << contents; }
};

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 {
    pos row = r * width;
    return contents[row+c];
}

inline Screen &Screen::set(char c) {
    contents[cursor] = c;
    return *this;
}

inline Screen &Screen::set(pos r, pos col, char ch) {
    contents[r*width + col] = ch;
    return *this;
}

class Window_mgr {
private:
    vector<Screen> screens{Screen(24, 80, ' ')};
};

int main()
{
    // Screen myscreen;
    // char ch = myscreen.get(); // calls Screen::get()
    // ch = myscreen.get(0,0); // calls Screen::get(pos,pos)
    // return 0;
    // Screen myScreen(5, 3);
    // const Screen blank(5, 3);
    // myScreen.set('#').display(cout);
    // blank.display(cout);
    Screen myScreen(5, 5, 'X');
    myScreen.move(4,0).set('#').display(cout);
    cout << '
';
    myScreen.display(cout);
    cout << '
';
    
    return 0;
}

// output:
// XXXXXXXXXXXXXXXXXXXX#XXXX
// XXXXXXXXXXXXXXXXXXXX#XXXX

7.28

move将返回一个副本,对myScreen的后续操作不会被改变myScreen本身,而是myScrenn的副本。

 

7.29

XXXXXXXXXXXXXXXXXXXX#XXXX
XXXXXXXXXXXXXXXXXXXXXXXXX

7.30

优点:在某些情况下可以区分成员和参数,例如:this->name = name;相比于f(string name): name(name) {}可以添加一些参数检查。

缺点:多数情况下是多余的,例如:f(string nm) nm =name;....

【Class Types】

一个类的成员类型不能是该类自己。

 7.31

一个典型的不得不用前置声明的例子。

class Y; // forward declaration, Y is an incomplete type.

class X {
    Y* pointer;
};

class Y {
    X object;
};

int main()
{
    X x;
    Y y;
    return 0;
}
原文地址:https://www.cnblogs.com/xkxf/p/6700751.html