RAII - 安卓中的智能指针

news2026/2/15 11:10:33

RAII - 安卓中的智能指针

概念 sp wp RefBase

是什么
system/core/libutils/RefBase.cpp
system/core/libutils/include/utils/RefBase.h

system/core/libutils/StrongPointer.cpp
system/core/libutils/include/utils/StrongPointer.h

Android在标准库之外,自定义了以下两种智能指针类型:

  • 强指针sp(strong pointer),弱指针 wp(weak pointer)都是模板类

  • sp 和share_ptr概念相同。wp 和weak_ptr概念相同。spwp has-a RefBase

    • 弱指针仅仅记录对象的地址,不能通过弱指针来访问该对象。要想访问弱指针所指向的对象,需首先通过wp类所提供的promote()方法将弱指针升级为强指针。

    • 弱指针所指向的对象是有可能在其它地方被销毁的。如果对象已经被销毁,wp的promote()方法将返回空指针,这样就能避免出现地址访问错的情况。

支持使用 spwp的对象,必须继承RefBase类。观察Android 中使用spwp的地方,传进来的模板参数都是继承自RefBase,例如:

// MediaPlayer是支持使用强指针和弱指针的对象
class MediaPlayer : public BnMediaPlayerClient,
                    public virtual IMediaDeathNotifier
{
public:
    MediaPlayer();
    ~MediaPlayer();
// 父类是 BnMediaPlayerClient 和 IMediaDeathNotifier,
// 多继承可能出现菱形继承,因此 虚继承自 RefBase
class IMediaDeathNotifier: virtual public RefBase
{
public:
    IMediaDeathNotifier() { addObitRecipient(this); }
    virtual ~IMediaDeathNotifier() { removeObitRecipient(this); }
    //随便找个,可以看到是继承RefBase,注意的是虚拟继承。
怎么用
// 使用智能指针的类必须继承自 RefBase
class Ressource : public RefBase { 
    Ressource(int){}
    void test() {}
}

{ // 作用域开始
    // 强引用 sp 构造函数有很多,这里简单列举两个
    Ressource* p = new Ressource(1);
    sp<Ressource> sp1(p);  // 1
    sp<Ressource> sp1 = new Ressource(1); // 2
    // sp::make 提供内存连贯性&缓存命中: 构建一个 sp 对象,其内部new一个资源对象,在将成员变量 m_ptr 指针指向它 
    static sp<Ressource> sp1 = sp<Ressource>::make(1); // 3
    // 通过强引用,操作指针指向的对象
    sp1->test();

    // 不能直接通过弱引用使用对象,升级为强指针再使用
    wp<Ressource> wp1(p); // 弱引用
    sp<Ressource> sp2 = wp1.promote();
    if(sp2 != NULL) {  // sp重载了 != 原本spRessource2与 NULL 不是同一类型
        sp2->test();
    }
}   // 作用域结束,调用强引用对象的析构函数,释放掉 p 指针指向的内存
对比标准库
为什么不直接用标准库?

为什么不用标准库?

1、历史原因!C++11推出 shared_ptr 和 weak_ptr之前,Android就已经需要存在了。

2、特殊的功能:有些功能是针对特定使用场景定制的(显然不符合 C++ 标准)。

例如,特殊的接口:

template<typename T>
class sp {
	//! Special optimization for use by ProcessState (and nobody else).
    void force_set(T* other);
}
template<typename T>
void sp<T>::force_set(T* other) {
    other->forceIncStrong(this);
    m_ptr = other;
}

例如,智能指针有两种模式

  • 强引用控制 OBJECT_LIFETIME_STRONG (默认,且绝大多数情况下都是强引用控制)
  • 弱引用控制 OBJECT_LIFETIME_WEAK

可通过 void RefBase::extendObjectLifetime(int32_t mode) 函数切换。强/弱引用控制的切换,通常在被管理的目标类的构造函数中调用:

// system/core/libutils/include/utils/RefBase.h
enum {
    OBJECT_LIFETIME_STRONG  = 0x0000, //强引用控制
    OBJECT_LIFETIME_WEAK    = 0x0001, //弱引用控制
    // 掩码值,可使用 flag&OBJECT_LIFETIME_MASK 的形式来获取 flag 的最低位数值
    OBJECT_LIFETIME_MASK    = 0x0001
};

// frameworks/native/libs/binder/BpBinder.cpp
BpBinder::BpBinder(Handle&& handle) {
    // 调用函数修改智能指针模式
    extendObjectLifetime(OBJECT_LIFETIME_WEAK);
}

例如,神奇的功能:

static void check_not_on_stack(const void* ptr) {
实现上的区别

从被管理的资源的角度来看:

标准库的 shared_ptr,”管理它所持有的 裸指针“。

  • 被管理的资源——裸指针所指向的对象,可以是任意类型。
  • 处于"被管理状态",不会对资源本身造成什么影响,没有负担。

Android 实现的 sp,wp ”操作它所持有的 RefBase类的计数“。

  • 被管理的资源——必须是 RefBase类,即被管理要求原有类型必须继承 RefBase类。
  • 处于"被管理状态",意味着
    • 资源类RefBase 自身保存计数,保存的强引用计数归零后自己执行delete this
    • sp wp 会去操作,让资源类去管理自身保存的计数
优缺点

Android这样不使用 STD 智能指针的做法,

优点:总结来说就是,客制化的美~

  1. 允许进行根据需求自定义的修改/优化。

    前文介绍过的,特殊接口,强/弱引用控制

  2. 安卓fwk中普遍存在跨进程“引用资源”的的情况,RefBase 自行计数方便IPC情境下的管理。

  3. 方便且臃肿的 debug 函数

缺点:总结来说就是,增大对象本身的开销

  1. 对象占用内存增高—— 作为操作系统必要,但是其它场景不需要的开销

    诸如函数RefBase::getStrongCount()RefBase::weakref_type::trackMe()

  2. 资源类有时候需要虚继承 RefBase —— 为了解决菱形继承而不可避免的选择。因此相较于使用标准库的智能指针,每次更改计数都不得不承担虚函数表带来的开销。

  3. 不同于标准库这件事情本身就会影响可读性,引入新的复杂度。

你自己看看复杂不复杂,臃肿不臃肿

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id); //删除弱引用
    //减少对象的弱引用计数,并且返回减少之前的值,存储在c中
    const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release); //计数减1
    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    /*
    如果c不等于1则不用进行处理,
    如果c=1,说明弱引用计数为0,则强引用计数必定也为0,
    则考虑如何释放该对象
    */
    if (c != 1) return; //还有其他引用在用这个指针,直接返回
     /*
    释放方式取决于当前对象的生命周期控制标志mFlags的值
    0,只要强引用计数为0,系统则自动释放这个对象
    OBJECT_LIFETIME_WEAK,只有强引用计数和弱引用计数都为0,系统才会自动释放这个对象
    OBJECT_LIFETIME_FOREVER,系统永远不会释放这个对象,需要开发者手动释放
    */   
    atomic_thread_fence(std::memory_order_acquire);
    // ...

RefBase类

首先,这屌东西里面有什么。它能干啥?

类方法
内部类
类方法
derived
成员变量
has-a
has-a
RefBase
incStrong/decStrong/createWeak
weakref_type
incWeak/decWeak
weakref_impl
weakref_type* mRefs 描述对象的引用计数
sp
m_ptr, RefBase*
wp
m_refs, weakref_type*
内部类 weakref_type

RefBase类 有两个内部类:定义 & 实现 引用计数维护接口

  • class weakref_type:定义接口
  • class weakref_impl:具体实现(weakref_type继承weakref_impl

成员变量RefBase::mRefs,类型就是刚刚提到的内部类 weakref_impl* const——描述对象的引用计数。

内部类weakref_type 提供有类方法:

  • 增加/减少 弱引用计数:weakref_type::incWeakweakref_type::decWeak
  • 维护对象的强/弱引用计数:weakref_type::attemptIncStrongweakref_type::attemptIncWeak
class RefBase {
    friend class weakref_type;
    class weakref_type {/*...*/};
    class weakref_impl;
    //使用一个weakref_impl对象来描述对象的引用计数
    weakref_impl* const mRefs;
class RefBase::weakref_impl : public RefBase::weakref_type
成员函数

RefBase类 有一系列 成员函数

  • 关于强引用:incStrongincStrongRequireStrongdecStrongforceIncStrong

  • 关于弱引用:createWeakgetWeakRefs

createWeak类方法,返回weakref_type类型的指针。

sp wp

sp

  • sp::m_ptr 的类型是 T* RefBase * ——sp has-a RefBase

  • 控制计数,最终调用:RefBase::incStrongRefBase::decStrong方法

template <typename T>
class sp {
    // T 类型就是 RefBase 的子类
    T* m_ptr;
};
// decStrong 和incStrong的具体实现,在RefBase类中。
void RefBase::incStrong(const void* id) const
void RefBase::decStrong(const void* id) const

wp

  • wp::m_refs的类型是RefBase::weakref_type *
  • 控制计数,最终调用:RefBase::incStrongRefBase::decStrong方法
// RefBase::weakref_type中实现
RefBase::weakref_type* RefBase::createWeak(const void* id) const
void RefBase::weakref_type::incWeak(const void* id)
void RefBase::weakref_type::decWeak(const void* id)
强引用 sp

通过 初始化/传入参数 设置m_ptrm_ptr必须是RefBase类的派生类(虚继承),从而使用 RefBase::incStrong和RefBase::decStrong修改RefBase本身的引用计数。

sp构造时:初始化sp中RefBase类型的指针 m_ptr,并调用指针incStrong()进行计数加1

sp析构时:指针T *m_ptr需要将计数减1

template<typename T>
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other)
        other->incStrong(this);
}

template<typename T>
sp<T>::~sp()
{
    if (m_ptr) m_ptr->decStrong(this);
}
弱引用 wp

成员变量 wp::m_ptrwp::m_refs

  • wp::m_ptr 类型是RefBase类型的指针,指向资源RefBase本身。
  • wp::m_refs 类型是RefBase::weakref_type,即资源RefBase的成员变量RefBase::mRefs——用于指向管理资源的内部类。

构造/移动&拷贝/赋值/清空时,以上两个成员变量被同时修改

通过成员变量wp::m_refs(类型为RefBase::weakref_type)的成员函数去修改资源RefBase计数。

template <typename T>
class wp
{
    typedef typename RefBase::weakref_type weakref_type;
    T*              m_ptr;
    weakref_type*   m_refs;
};

template<typename T>
wp<T>::~wp()
{
    if (m_ptr) m_refs->decWeak(this);
}

源码注释 RefBase

接口
class RefBase
{
public:
	//增加强引用计数
	void            incStrong(const void* id) const;
	//减少强引用计数
    void            decStrong(const void* id) const;
    
    // (wp中的m_refs调用)
    weakref_type*   createWeak(const void* id) const;
    weakref_type*   getWeakRefs() const;
    
    void            forceIncStrong(const void* id) const;
    int32_t         getStrongCount() const;
    
    // 定义了内部类:weakref_type weakref_impl
    class weakref_type
    {
    public:
        RefBase*            refBase() const;
		//增加弱引用计数 (wp中的m_refs调用)
        void                incWeak(const void* id);
		//减少弱引用计数 (wp中的m_refs调用)
        void                decWeak(const void* id);
		//维护对象的强引用计数
        bool                attemptIncStrong(const void* id);
		//维护对象的弱引用计数
        bool                attemptIncWeak(const void* id);
        // debug用函数
        int32_t             getWeakCount() const;
        void                printRefs() const;
        void                trackMe(bool enable, bool retain);
    };
    
    // Debug 函数...
    // 允许自定义的Destroyer
    class Destroyer {
        friend class RefBase;
        public:
            virtual ~Destroyer();
        private:
            virtual void destroy(RefBase const* base) = 0;
    };

    void setDestroyer(Destroyer* destroyer);
protected:
    RefBase();
    virtual ~RefBase();

    // 生命周期有关的flag,函数...

    // 一堆其它功能的虚函数...
private:
    friend class weakref_type;
    class weakref_impl;
    
    RefBase(const RefBase& o);
    RefBase& operator=(const RefBase& o);
	//使用一个weakref_impl对象来描述对象的引用计数
    weakref_impl* const mRefs;
};
强引用
decStrong

RefBase类的成员函数decStrong减少对象的引用计数

实现如下:

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);//移除强引用链表中的引用id
    const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);//计数减1
#if PRINT_REFS
    ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
    ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
    if (c == 1) {//如果是最后一个了,那需要释放该指针了
        std::atomic_thread_fence(std::memory_order_acquire);
        refs->mBase->onLastStrongRef(id);
        int32_t flags = refs->mFlags.load(std::memory_order_relaxed);
        if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            delete this;
        }
    }
    refs->decWeak(id);//如果不是最后一个,需要减少弱引用链表
}
incStrong

RefBase类的成员函数incStrong 增加对象的引用计数

实现如下:

frameworks/base/libs/utils/RefBase.cpp
//RefBase类的成员函数incStrong来增加对象的引用计数
void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->addWeakRef(id);
    refs->incWeak(id);//增加对象的引用计数
    refs->addStrongRef(id);//增加对象的强引用计数
    
    const int32_t c = android_atomic_inc(&refs->mStrong);
    LOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);

#if PRINT_REFS
    LOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif

    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }

    android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
//如果对象第一次被强指针引用,进行通知
    const_cast<RefBase*>(this)->onFirstRef()
}
弱引用
incWeak

只有RefBase::weakref_type的对象才能调用 incWeakdecWeak

frameworks/base/libs/utils/RefBase.cpp
void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->addWeakRef(id);
    const int32_t c = android_atomic_inc(&impl->mWeak);
    LOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
decWeak

只有RefBase::weakref_type的对象才能调用 incWeakdecWeak

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id); //删除弱引用
    //减少对象的弱引用计数,并且返回减少之前的值,存储在c中
    const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release); //计数减1
    ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
    /*
    如果c不等于1则不用进行处理,
    如果c=1,说明弱引用计数为0,则强引用计数必定也为0,
    则考虑如何释放该对象
    */
    if (c != 1) return; //还有其他引用在用这个指针,直接返回
     /*
    释放方式取决于当前对象的生命周期控制标志mFlags的值
    0,只要强引用计数为0,系统则自动释放这个对象
    OBJECT_LIFETIME_WEAK,只有强引用计数和弱引用计数都为0,系统才会自动释放这个对象
    OBJECT_LIFETIME_FOREVER,系统永远不会释放这个对象,需要开发者手动释放
    */   
    atomic_thread_fence(std::memory_order_acquire);
 
    int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
    if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
        // This is the regular lifetime case. The object is destroyed
        // when the last strong reference goes away. Since weakref_impl
        // outlive the object, it is not destroyed in the dtor, and
        // we'll have to do it here.
        if (impl->mStrong.load(std::memory_order_relaxed)
                == INITIAL_STRONG_VALUE) {
            // Special case: we never had a strong reference, so we need to
            // destroy the object now.
            delete impl->mBase; //如果没有其他的引用了,需要释放当前指针
        } else {
            // ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
            delete impl;
        }
    } else {
        // This is the OBJECT_LIFETIME_WEAK case. The last weak-reference
        // is gone, we can destroy the object.
        impl->mBase->onLastWeakRef(id);
        if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
            if (impl->mBase) {
                if (impl->mDestroyer) {
                    impl->mDestroyer->destroy(impl->mBase);
                } else {
                    delete impl->mBase;//如果没有其他引用了,就释放当前指针
                }
            }
        }
    }
}
weakref_type 具体实现

weakref_type类定义了引用计数维护接口,具体的实现由weakref_impl类实现。

frameworks/base/libs/utils/RefBase.cpp
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    volatile int32_t    mStrong; 	//描述强引用计数
    volatile int32_t    mWeak;		//描述弱引用计数
    RefBase* const      mBase;		//指向它所引用的对象的地址
    /*
    mFlags取值范围
    	0,表示对象的生命周期只收强引用计数影响
    	OBJECT_LIFETIME_WEAK,	表示对象的生命周期同时受强引用计数和弱引用计数影响
    	OBJECT_LIFETIME_FOREVER,表示对象的生命周期完全不受强引用指针和弱引用指针影响
    */
    volatile int32_t    mFlags;//描述对象生命周期控制方式
    Destroyer*          mDestroyer;

#if !DEBUG_REFS
    weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(0)
        , mDestroyer(0)
    {
    }

    void addStrongRef(const void* /*id*/) { }
    void removeStrongRef(const void* /*id*/) { }
    void addWeakRef(const void* /*id*/) { }
    void removeWeakRef(const void* /*id*/) { }
    void printRefs() const { }
    void trackMe(bool, bool) { }

#else
....//省略部分不重要内容
#endif
};
析构函数

RefBase类成员变量mRefs的解析函数的实现。

frameworks/base/libs/utils/RefBase.cpp
RefBase::~RefBase()
{
    /*
        如果发现对象的弱引用计数为0,则在释放RefBase对象的同时也将引用计数对象mRefs一起释放掉。
        mRefs对象在RefBase的构造函数中被创建,指向weakref_impl,
        如果RefBase对象都不存在了,并且mWeak也为0,mRefs也没有什么存在的必要了。
        强引用计数一定是小于弱引用计数的,所以强引用计数为0时弱引用计数仍有可能大于0。
        所以在弱引用计数大于0的情况下我们只释放RefBase对象,留下mRefs对象继续使用。
    */
    if ((mRefs->mFlags & OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK) {
        if (mRefs->mWeak == 0) {
            delete mRefs;
        }
        ...
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2211496.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++打怪之路Lv8】-- string类

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;重生之我在学Linux&#xff0c;C打怪之路&#xff0c;python从入门到精通&#xff0c;数据结构&#xff0c;C语言&#xff0c;C语言题集&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持…

智能汽车智能网联

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何消耗你的人和事&#xff0c;多看一眼都是你的不…

深入理解线性表--顺序表

目录 顺序表- Seqlist -> sequence 顺序 list 表 顺序表的概念 问题与解答 顺序表的分类 静态顺序表 动态顺序表 问题与解答(递进式) 动态顺序表的实现 尾插 头插 尾删 头删 指定位置插入 指定位置删除 销毁 总结 前言&#xff1a;线性表是具有相同特性的一类数据结构…

【exceljs】纯前端如何实现Excel导出下载和上传解析?

前段时间写过一篇类似的文章&#xff0c;介绍了sheetjs。最近发现了一个更好用的库ExcelJS&#xff0c;它支持高级的样式自定义&#xff0c;并且使用起来也不复杂。实际上sheetjs也支持高级自定义样式&#xff0c;不过需要使用付费版。 下面对比了Exceljs和Sheetjs&#xff1a…

Linux的习题+一道回溯类型的算法题

Linux的习题 Linux环境与版本 1.linux 2.6.* 内核默认支持的文件系统有哪些&#xff1f;[多选] A.ext3 B.ext2 C.ext4 D.xfs E.ufs 正确答案&#xff1a;ABCD A 全称Linux extended file system, extfs,即Linux扩展文件系统&#xff0c;ext2为第二代 D XFS一种高性能的日…

使用频率最高的 opencv 基础绘图操作 - python 实现

以下是 opencv-python 基本操作绘制示例&#xff0c;绘制&#xff1a; 1&#xff09;圆&#xff0c;2&#xff09;矩形&#xff0c;3&#xff09;线段&#xff0c;4&#xff09;文本。 安装 opencv-python pip install opencv-python 在图上绘制圆的操作&#xff0c;示例如…

HCIP-HarmonyOS Application Developer 习题(五)

1、以下哪种原子化布局能力属于自适应变化能力? A. 拉伸 B.占比 C. 隐藏 D.拆行 答案&#xff1a;A 分析&#xff1a;划分为“自适应变化能力”和“自适应布局能力”两类。 其中&#xff0c;自适应变化能力包含了缩放能力和拉伸能力&#xff0c;自适应布局能力包含了隐藏、折…

『Mysql进阶』Mysql explain详解(五)

目录 Explain 介绍 Explain分析示例 explain中的列 1. id 列 2. select_type 列 3. table 列 4. partitions 列 5. type 列 6. possible_keys 列 7. key 列 8. key_len 列 9. ref 列 10. rows 列 11. filtered 列 12. Extra 列 Explain 介绍 EXPLAIN 语句提供有…

Spring学习——SpringAOP

0. IOC思想(DI)1. 关键注解Repositorypublic class DeptDaoImpl1 implements DeptDao{}RepositoryPrimarypublic class DeptDaoImpl2 implements DeptDao{}Servicepublic class DeptServiceImpl implements DeptService{AutowiredQulifier("deptDaoImpl2")private De…

UE5学习笔记24-添加武器弹药

一、给角色的武器添加弹药 1.创建界面&#xff0c;根据笔记23的界面中添加 2.绑定界面控件 UPROPERTY(meta (Bindwidget))UTextBlock* WeaponAmmoAmount;UPROPERTY(meta (Bindwidget))UTextBlock* CarriedAmmoAmount; 3.添加武器类型枚举 3.1创建武器类型枚举头文件 3.2创建文…

C++容器:map

1.铺垫 1.1map和unorder_map&#xff0c;两者的实现思想不同&#xff0c;但是用法是相同的&#xff1b;map的思想是平衡二叉树&#xff1b;unorder_map的思想是哈希&#xff1b; 1.2在现实及做题中&#xff0c;百分之99&#xff0c;都在使用unorder_map&#xff0c;接下来的讲…

2024 第一次周赛

A: 题目大意 骑士每连续 i 天每天会得到 i 个金币&#xff0c;&#xff08;i 1&#xff0c; 2&#xff0c; 3 &#xff0c; …&#xff09;,那么展开看每一天可以得到的金币数&#xff1a;1 2 2 3 3 3 4 4 4 5 5 5 5 5 … 可以发现就是1个1 &#xff0c;2个2, 3个3…,那么我…

P20机型HW睡眠省电-调研

摘要 AI睡眠场景识别 不激进的智能管控 睡眠省电 UI 以前有单独的睡眠开关选项&#xff0c;现在没有了&#xff0c;但是智能充电模式是使用AI睡眠模式 睡眠识别 华为专利上提到的论文《BTP-A Bedtime Predicting Algorithm》 各种事件状态&#xff08;亮灭屏、alarm、主动…

《数字信号处理》学习08-围线积分法(留数法)计算z 逆变换

目录 一&#xff0c;z逆变换相关概念 二&#xff0c;留数定理相关概念 三&#xff0c;习题 一&#xff0c;z逆变换相关概念 接下来开始学习z变换的反变换-z逆变换&#xff08;z反变化&#xff09;。 由象函数 求它的原序列 的过程就称为 逆变换。即 。 求z逆变换…

RTR-Chapter8

第八章 光与颜色 光量辐射度量学光度学色度学使用RGB颜色进行渲染 从场景到屏幕HDR显示编码色调映射色调再现变换曝光 颜色分级 光量 辐射度量学 辐射度量学&#xff08;radiometry&#xff09;研究的是对电磁辐射&#xff08;electromagnetic radiation&#xff09;的测量&am…

linux线程 | 线程的控制(二)

前言&#xff1a; 本节内容是线程的控制部分的第二个小节。 主要是列出我们的线程控制部分的几个细节性问题以及我们的线程分离。这些都是需要大量的代码去进行实验的。所以&#xff0c; 准备好接受新知识的友友们请耐心观看。 现在开始我们的学习吧。 ps:本节内容适合了解线程…

开源情报(OSINT)入门:初学者指南

欢迎来到令人兴奋的开源情报 (OSINT) 世界&#xff01;如果您是该领域的新手&#xff0c;OSINT 就是收集和分析公开信息以获取见解并制作情报产品。 无论您是安全爱好者、记者还是只是对 OSINT 感兴趣&#xff0c;本入门指南都将带您了解基础知识&#xff0c;让您开始第一次&a…

MySQL进阶学习一(2024.10.07版)

2024-10-06 -------------------------------------------------------------------------------------------------------------------------------- 1.一条SQL语句是如何执行的 单进程的多线程模型 MySQL的物理目录 show global variables like "%basedir%"; …

初学Vue(3)(内置指令)

文章目录 十四、内置指令v-textv-htmlv-cloak&#xff08;没有值&#xff09;v-oncev-pre自定义指令一、定义语法二、配置对象中常用的3个问题三、备注 十四、内置指令 回顾&#xff1a; v-bind &#xff1a;单向绑定解析表达式&#xff0c;可简写为 :xxx v-model : 双向数据绑…

HTML快速入门--第一节--五个基本标签

一、网络编程的三大基石 1.1 url 统一资源定位符&#xff1a; 网址:整个互联网中可以唯一且准确的确定一个资源的位置 (url项目外) 网址:https://www.baidu.com/ https://www.baidu.com/ 协议://ip端口/项目名/页面名 协议:交通法规获取资源 ip端口 &#xff08;域名&…