文章目录
- 前言
- 一、如何理解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。
示例:5 & 3(即 0101 & 0011)的结果为 0001,即1 -
按位或(|)
对两个操作数的每一位执行逻辑或操作。
只要两个对应的二进制位中有一个为1,结果位就为1。
示例:5 | 3(即 0101 | 0011)的结果为 0111,即7 -
按位异或(^)
对两个操作数的每一位执行逻辑异或操作。
当两个对应的二进制位不同时,结果位为1;相同时为0。
示例:5 ^ 3(即 0101 ^ 0011)的结果为 0110,即6 -
按位取反(~)
对操作数的每一位执行取反操作。
1变为0,0变为1。
示例:~5(即 ~0101)在32位系统中为 11111111 11111111 11111111 11111010,即-6(补码形式) -
左移(<<)
将操作数的二进制位向左移动指定的位数,右边补0。
左移n位相当于乘以2的n次方。
示例:5 << 1(即 0101 << 1)的结果为 1010,即10 -
右移(>>)
将操作数的二进制位向右移动指定的位数。
正数高位补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;