muduo库复习的基础知识2

文章目录

  • 前言
  • 一、如何理解std::move?
    • 1.std::move 概述
    • 2. std::move 基本语法
    • 理解移动语义
    • 3. 例子
      • (1)普通复制
      • (2)使用std::move实现移动
      • (3)移动构造函数
  • 二、使用到的位运算符
  • 三、智能指针std::shared_ptr
    • 什么是std::shared_ptr?
    • std::shared_ptr如何工作?
    • std::shared_ptr基本语法
      • 创建
      • 拷贝和赋值
      • 重置和释放所有权
      • 访问对象
      • use_count() 引用计数,当前有多少个shared_ptr正在共享同一个对象


前言

继续复习muduo库。下面是Channel模块的完整代码。
Channel.h

#pragma once
#include "noncopyable.h"
#include "Timestampp.h"
#include <functional>
#include <memory>class EventLoop;//Channel理解为通道,封装了sockfd和其感兴趣的event,如EPOLLIN、EPOLLOUT事件
//还绑定了Poller返回的具体事件
class Channel: noncopyable
{
public:using EventCallback = std::function<void()>;using ReadEventCallback = std::function<void(Timestampp)>;Channel(EventLoop *Loop, int fd);~Channel();//fd得到poller通知以后,处理事件的回调void handleEvent(Timestampp receiveTime);//设置回调函数对象void setReadCallback(ReadEventCallback cb) {readCallback_=std::move(cb);};void setWriteCallback(EventCallback cb) {writeCallback_=std::move(cb);};void setCloseCallback(EventCallback cb) {closeCallback_=std::move(cb);};void setErrorCallback(EventCallback cb) {errorCallback_=std::move(cb);};//防止当channel被手动remove掉,channel还在执行回调操作void tie(const std::shared_ptr<void>&);int fd() const {return fd_;}int events() const {return events_;}void set_revents(int revt) {revents_=revt;}//设置fd对应的事件状态void enableReading() { events_ |= kReadEvent; update(); }void disableReading() { events_ &= ~kReadEvent; update(); }void enableWriting() { events_ |= kWriteEvent; update(); }void disableWriting() { events_ &= ~kWriteEvent; update(); }void disableAll() { events_ = kNoneEvent; update(); }//返回fd当前的事件状态bool isNoneEvent() const { return events_ == kNoneEvent; }bool isWriting() const { return events_ & kWriteEvent; }bool isReading() const { return events_ & kReadEvent; }int index() {return index_;}void set_index(int idx) {index_=idx;}EventLoop* ownerLoop() {return loop_;}void remove();private:void update();void handleEventWithGuard(Timestampp receiveTime);static const int kNoneEvent;static const int kReadEvent;static const int kWriteEvent;EventLoop* loop_; //事件循环const int fd_;    //fd, Poller监听的对象int events_;      //注册fd感兴趣的事件int revents_;     //poller返回的具体发生的事件int index_;std::weak_ptr<void> tie_;bool tied_;//因为channel通道里面能够获知fd最终发生的具体的事件revents,所以它负责调用具体事件的回调操作ReadEventCallback readCallback_;EventCallback writeCallback_;EventCallback closeCallback_;EventCallback errorCallback_;
};

一、如何理解std::move?

不太理解这句话:
void setReadCallback(ReadEventCallback cb) {readCallback_ = std::move(cb);};
ai的回答结果如下:

1.std::move 概述

https://en.cppreference.com/w/cpp/utility/move.html 查看move的具体用法。
std::move 本质上是一个类型转换工具,用来将左值转换成右值引用。它本身并不会进行任何实际的“移动”操作。其主要目的是通过启用移动语义,允许资源的转移而不是拷贝,从而提高性能,特别是在处理大对象时。(如 std::vector 或 std::string)

2. std::move 基本语法

std::move(T&& obj);
  • T&& 是一个右值引用类型,这意味着 std::move 接受一个左值,并将其转换为右值引用。
  • std::move 使得一个对象能够被转移(即其资源被“移动”)而不是被复制。

理解移动语义

  • 移动操作与复制操作不同,移动只是简单地“转移”资源(如内存、文件句柄等),而复制则是为对象创建一个完全独立的副本。
  • 使用移动语义可以避免不必要的资源复制,提升性能,尤其是对于大对象或需要复杂资源管理的类型(如 std::vector、std::string 等)。

3. 例子

(1)普通复制

std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = v1;  // 复制v1的内容到v2

(2)使用std::move实现移动

std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = std::move(v1);  // v1的资源被“移动”到v2
// 此时v1的状态是未定义的,不能再安全地使用它

(3)移动构造函数

如果你定义了一个类,并且为该类提供了移动构造函数和移动赋值运算符,std::move 将使得该类的对象能够高效地转移资源,而不是进行昂贵的复制。

class MyClass {
public:int* data;MyClass(int value) : data(new int(value)) {}// 移动构造函数MyClass(MyClass&& other) noexcept : data(other.data) {other.data = nullptr;  // 使原对象处于一个有效但空的状态}~MyClass() { delete data; }
};
MyClass obj1(10);
MyClass obj2 = std::move(obj1);  // obj1的资源被移动到obj2

在上述代码中,通过 std::move,obj1 的资源(data 指针)被移动到 obj2,而 obj1 的 data 被设置为 nullptr,表示它不再拥有资源。

二、使用到的位运算符

以下是C++中常用的位运算符及其功能:

  1. 按位与(&)
    对两个操作数的每一位执行逻辑与操作。
    只有当两个对应的二进制位都为1时,结果位才为1。
    示例:5 & 3(即 0101 & 0011)的结果为 0001,即1

  2. 按位或(|)
    对两个操作数的每一位执行逻辑或操作。
    只要两个对应的二进制位中有一个为1,结果位就为1。
    示例:5 | 3(即 0101 | 0011)的结果为 0111,即7

  3. 按位异或(^)
    对两个操作数的每一位执行逻辑异或操作。
    当两个对应的二进制位不同时,结果位为1;相同时为0。
    示例:5 ^ 3(即 0101 ^ 0011)的结果为 0110,即6

  4. 按位取反(~)
    对操作数的每一位执行取反操作。
    1变为0,0变为1。
    示例:~5(即 ~0101)在32位系统中为 11111111 11111111 11111111 11111010,即-6(补码形式)

  5. 左移(<<)
    将操作数的二进制位向左移动指定的位数,右边补0。
    左移n位相当于乘以2的n次方。
    示例:5 << 1(即 0101 << 1)的结果为 1010,即10

  6. 右移(>>)
    将操作数的二进制位向右移动指定的位数。
    正数高位补0,负数高位补1(补符号位)。
    示例:5 >> 1(即 0101 >> 1)的结果为 0010,即2

因此,下面的代码就相当于:events_ = events_ | kReadEvent; events_ = events_ & ~kReadEvent;

events_ |= kReadEvent;
events_ &= ~kReadEvent;

三、智能指针std::shared_ptr

什么是std::shared_ptr?

std::shared_ptr是c++中的一种智能指针,它通过引用计数提供对动态分配对象的自动内存管理。有以下三个特点:

  • 自动内存管理: shared_ptr会自动管理它所指向对象的内存。当最后一个指向该对象的shared_ptr被销毁时,对象会自动释放。
  • 引用计数 : shared_ptr使用引用计数来跟踪有多少个shared_ptr对象共享对同一个对象的所有权。每当新的shared_ptr被创建或拷贝时,引用计数会增加;每当shared_ptr被销毁或重置,引用计数会减少。
  • 共享所有权 :多个shared_ptr对象可以共享对同一个对象的所有权,因此被称作共享指针。

std::shared_ptr如何工作?

引用计数 :

  • 每个 shared_ptr 都会持有一个指向对象的指针,并且管理一个 控制块,该控制块中保存了引用计数。
  • 当一个 shared_ptr 被拷贝或赋值时,引用计数会增加。
  • 当一个 shared_ptr 超出作用域或显式重置时,引用计数会减少。
  • 当引用计数降到零时,管理的对象会被销毁。

std::shared_ptr基本语法

#include <memory>
std::shared_ptr<T> ptr1 = std::make_shared<T>(args);  // 创建 shared_ptr 来管理 T 类型的对象
std::shared_ptr<T> ptr2 = ptr1;  // ptr2 与 ptr1 共享所有权

创建

你可以使用以下方式创建 shared_ptr:

auto ptr = std::make_shared<int>(10); // 创建一个管理整数的 shared_ptr,值为 10std::make_shared<T>():这是推荐且高效的创建 shared_ptr 的方式,它在一次分配中同时创建对象和 shared_ptr(避免了两次分配)。

你也可以直接创建 shared_ptr:

std::shared_ptr<int> ptr = std::shared_ptr<int>(new int(10)); // 不推荐,效率较低

拷贝和赋值

std::shared_ptr<int> ptr1 = std::make_shared<int>(10);
std::shared_ptr<int> ptr2 = ptr1; // ptr1 和 ptr2 共享所有权

重置和释放所有权

ptr1.reset();  // 释放当前对象的所有权,ptr1 现在不再管理任何对象
ptr2.reset(new int(20));  // ptr2 现在管理一个新对象,值为 20

访问对象

*ptr1 = 30;    // 解引用指针来修改对象
int value = *ptr1; // 解引用来读取值

use_count() 引用计数,当前有多少个shared_ptr正在共享同一个对象

std::cout << "引用计数: " << ptr1.use_count() << std::endl;