C++ Primer 第7章 类 - 中(零基础学习C++,精简学习笔记)

在这里插入图片描述

作者简介:努力的clz ,一个努力编程的菜鸟
 
文章专栏:C++ Primer 学习笔记
 
专栏简介: 本专栏是博主学习 C++ Primer 的学习笔记,因为这本书内容超级多,所以博主将其中的 重点 概括提炼出来,于是有了这个专栏的诞生。
 
C++ Primer 学习笔记 第7章 类 笔记导航

  1. C++ Primer 第7章 类 - 上
  2. C++ Primer 第7章 类 - 中 ⇦当前位置
  3. C++ Primer 第7章 类 - 下 (加班中)
  4. C++ Primer 总目录 传送门 ‍‍‍
     

如果本篇文章对大家起到帮助的话,跪求各位帅哥美女们,求赞 、求收藏 、求关注!

 

第7章 类 (中)

7.3 类的其它特性

7.3.1 类成员再探

本节将会创建两个新类:Screen、Window_mgr 类 来展示类的其它特性。

 

1. 定义一个类型成员

Screen 类 表示显示器中的一个窗口。

#include 

using std::string;

class Screen
{
public:
    typedef string::size_type pos;

private:
    pos cursor = 0;            // 光标的位置
    pos height = 0, width = 0; // 屏幕的高和宽
    string contents;           // 保持 Screen 的内容
};

 

关于 pos 的声明有两点需要注意:

  • 可以等价第使用类型别名 代替上方代码写法;
  • 用来定义类型的成员必须先定义后使用。
class Screen
{
public:
    typedef string::size_type pos;

	// 上方代码等价于
	using pos = string::size_type;	// 使用类型别名等价地声明一个类型名字

private:
    // 略
};

 

2. Screen 类的成员函数

#include 

using std::string;

class Screen
{
public:
    typedef string::size_type pos;	// 定义一个类型成员

    Screen() = default; // 因为 Screen 有另一个构造函数,所以必须显式声明默认构造函数

    // 初始值列表未对 cursor 赋初值,所以会隐式地使用类内初始值
    Screen(pos ht, pos wd, char c) : height(ht), width(wd), contents(ht * wd, c) {}

    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;           // 保持 Screen 的内容
};

 

3. 令成员作为内联函数

定义在类内部的成员函数是 自动 inline 的,例如 Screen 类 的构造函数和 get 函数。

  • 在类的 内部,可以把 inline 关键字 作为声明的一部分显式地声明成员函数;
  • 在类的 外部,可以用 inline 关键字 修饰函数的定义.
/**
 * @brief 移动光标到指定的列
 *
 * @param r
 * @param c
 * @return Screen&
 */
inline Screen &Screen::move(pos r, pos c) // 可以在函数的定义处指定inline
{
    pos row = r * width; // 计算行的位置
    cursor = row + c;    // 在行内将光标移动到指定的列
    return *this;        // 以左值的形式返回对象
}

/**
 * @brief 返回光标所指字符
 *
 * @param r
 * @param c
 * @return char
 */
char Screen::get(pos r, pos c) const // 在类的内部声明成inline
{
    pos row = r * width;      // 计算行的位置
    return contents[row + c]; // 返回给定列的字符
}

 

4. 重载成员函数

成员函数也可以和非成员函数一样进行 函数重载 ,只要函数之间 参数数量、类型 有区别即可。

 

5. 可变数据成员

当在变量声明前增加一个 mutable 关键字 ,就成为了 可变数据成员 ,其永远不会是 const
 
即使在 const成员函数 内,也可以对 可变数据成员 进行修改。

/**
 * @brief 保持一个计数值,用于记录成员函数被调用的次数
 *
 */
void Screen::some_member() const
{
    ++access_ctr; // mutable 可变数据成员
}

C++ Primer 第7章 类 - 中(零基础学习C++,精简学习笔记)_第1张图片

 

6. 类数据成员的初始值

下方代码中,使用一个单独的元素值对 vector 成员 执行了列表初始化,这个 Screen 的值被传递给 vector 的构造函数,从而创建一个单元素的 vector对象

// todo 窗口管理类
class Window_mgr
{
private:
    // 这个 Window_mgr 追踪的 Screen
    // 默认情况下,一个 Window_mgr 包含一个标准尺寸
    vector<Screen> screens{Screen(24, 80, ' ')};
};

 


7.3.2 返回 *this 的成员函数

添加两个重名的 set 函数,set 成员返回值是调用 set 的对象的引用,也就是函数返回的是对象本身而非对象的副本。

/**
 * @brief 设置光标所在位置的字符
 *
 * @param c
 * @return Screen&
 */
inline Screen &Screen::set(char c)
{
    contents[cursor] = c; // 设置当前光标所在位置的新值
    return *this;         // 将this对象作为左值返回
}

/**
 * @brief 设置给定位置的字符
 *
 * @param r
 * @param col
 * @param ch
 * @return Screen&
 */
inline Screen &Screen::set(pos r, pos col, char ch)
{
    contents[r * width + col] = ch; // 设置给定位置的新值
    return *this;                   // 将this对象作为左值返回
}

C++ Primer 第7章 类 - 中(零基础学习C++,精简学习笔记)_第2张图片

 

1. 从const 成员函数返回 * this

接下来会添加一个 display 操作 ,负责打印 Screen 的内容,令其为一个 const 成员
 
一个 const 成员函数如果以引用的形式返回 *this,那么它的返回类型将是常量引用。

 

2. 基于 const 的重载

    /**
     * @brief 负责显示 Screen 的内容
     *
     * @param os
     */
    void do_display(ostream &os) const
    {
        os << contents;
    }

C++ Primer 第7章 类 - 中(零基础学习C++,精简学习笔记)_第3张图片

 

    // 根据对象是否是 const 重载了 display 函数
    Screen &display(ostream &os)
    {
        do_display(os);
        return *this;
    }

    const Screen &display(ostream &os) const
    {
        do_display(os);
        return *this;
    }

C++ Primer 第7章 类 - 中(零基础学习C++,精简学习笔记)_第4张图片

 


7.3.3 类类型

每个类定义了唯一的类型。即使两个类的成员列表完全一致,也是不同的类型。

struct First 
{
	int memi;
	int getMem();
};

struct Second
{
	int memi;
	int getMem();
};

First obj1;
Second obj2= boj1;	// 错误: obj1 和 obj2 的类型不同

 

类的声明

类的声明和定义可以分开进行,先声明后定义。
 
这种声明称为 前向声明,类型 Screen 在声明之后、定义之前处于 不完全类型
 
不完全类型 只能知道 Screen 是一个类类型,但不清楚类内包含哪些成员(因为还没定义)。

class Screen;	// Screen 类的声明

 


7.3.4 友元再探

除了将非成员函数定义成 友元 ,还有其它应用场景:

  • 把其它类定义成友元;
  • 把其它类的成员函数定义成友元;

 

1. 类之间的友元关系

在 Screen 类中设置 友元类 ,友元类的成员函数可以访问包括非公有成员在内的所有成员。

C++ Primer 第7章 类 - 中(零基础学习C++,精简学习笔记)_第5张图片

 

注意!友元关系不具备传递性
 
Screen 设置 友元类Window_mgr,如果 Window_mgr 设置 友元类 Student,但 Screen 与 Student 并不是友元关系。

// todo 窗口管理类
class Window_mgr
{
public:
    // 窗口中每个屏幕的编号
    using ScreenIndex = vector<Screen>::size_type;

    // 按照编号将指定的Screen重置为空白
    void clear(ScreenIndex);

private:
    // 这个 Window_mgr 追踪的 Screen
    // 默认情况下,一个 Window_mgr 包含一个标准尺寸
    vector<Screen> screens{Screen(24, 80, ' ')};
};

/**
 * @brief
 *
 * @param i
 */
void Window_mgr::clear(ScreenIndex i)
{
    // s是一个Screen的引用,指向我们想清空的那个屏幕
    Screen &s = screens[i];

    // 将那个选的ing的Screen重置为空白
    s.contents = string(s.height * s.width, ' ');
}

 

2. 令成员函数作为友元

设置友元类,Window_mgr 所有的成员函数都可以访问 Screen的成员变量,但可以只单独设置一个函数为友元。
 
令某个成员函数作为友元,需要按照一定的先后顺序规则:

  • 先定义 Window_mgr 类,声明 clear 函数,但不能定义;
  • 定义 Screen 类,包括对 clear 的友元声明;
  • 定义clear,此时才可以 Screen 的成员。
class Screen{
	// Window_mgr::clear 必须在 Screen 类之前被声明
	friend void Window_mgr::clear(ScreenIndex);
	
	// Screen类的剩余部分 
}

 

3. 函数重载和友元

如果一个类想把一组重载函数声明成它的友元,需要对其一个个分别声明。

 

4. 友元声明和作用域

类和非成员函数的声明不是必须在它们的友元声明之前。
 
分析下方代码,要理解友元声明的作用是影响访问权限,并非普通意义上的声明。

struct X
{
	friend void f() { /* 友元函数可以定义在类的内部 */}
	
	X() {
		f();	// 错误: f 还没被声明
	}
	void g();
	void h();
};

void X::g() {
	return f();	// 错误: f 还没有被声明
}
void f();		// 声明那个定义在 X 中的函数
void X::h() {
	return f();	// 正确: 现在 f 的声明在作用域内了
}

 


C++ Primer 学习笔记 第7章 类 笔记导航

  1. C++ Primer 第7章 类 - 上
  2. C++ Primer 第7章 类 - 中 ⇦当前位置
  3. C++ Primer 第7章 类 - 下 (加班中)
  4. C++ Primer 总目录 传送门 ‍‍‍
     

如果本篇文章对大家起到帮助的话,跪求各位帅哥美女们,求赞 、求收藏 、求关注!

C++ Primer 第7章 类 - 中(零基础学习C++,精简学习笔记)_第6张图片
在这里插入图片描述

你可能感兴趣的:(#,C++,Primer,学习笔记,c++,学习,笔记)