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

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

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 <string>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 <string>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++,精简学习笔记)

 

6. 类数据成员的初始值

下方代码中,使用一个单独的元素值对 vector 成员 执行了列表初始化,这个 Screen 的值被传递给 vector<Screen> 的构造函数,从而创建一个单元素的 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++,精简学习笔记)

 

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++,精简学习笔记)

 

    // 根据对象是否是 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++,精简学习笔记)

 


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++,精简学习笔记)

 

注意!友元关系不具备传递性
 
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++,精简学习笔记)