WebRTC中的scoped_refptr解析

本文介绍了C++如何通过Webrtc的scoped_refptr实现类似Java的内存自动回收机制,通过RAII原理和Scoped_A类实例,展示了如何在对象作用域结束时自动释放内存,以及ref的引用计数应用。

 众所周知,C++中在堆中申请的内存都需要程序员自己手动删除,这是C++容易造成内存泄漏的根本原因。使用过Java的朋友都清楚,Java有完善的内存回收机制,无需程序员调用释放内存的操作。C++里是否能够实现类似Java的自动回收内存的机制呢?答案是肯定的,而Webrtc中的scoped_refptr就是实现类似的功能。

首先我们从这个类的名字入手去理解它的含义。名字由三个单词组成,分别为scope,ref,ptr。

先看ptr,C++程序员都知道这是pointer指针的意思,但是类名中包含指针的语义,这个该如何理解呢?笔者不才,说实话,这个我也是想了好久才弄明白是怎么回事。先说结论吧,在webrtc中,凡是以ptr结尾的类,若以此类创建的非指针对象,均可以当指针对象的语法使用。估计读者有点蒙,下面以实际例子说明。

一般来说,C++中有两种创建对象的方法,一种是创建指针对象

A *a = new A();
a->func();

 另一种是创建非指针对象


A a;
a.func()

    这两种方法创建的对象,对象调用方法的语法是不一样的。一个是以->方式调用,一个是以.方式调用。但是请看:

scoped_refptr<A> temp;
temp->func();

    注意到了吗?temp是以非指针对象的方式创建的,但是它可以使用指针对象的语法调用函数方法。这个是如何实现的呢?这个其实一点也不难,scoped_refptr的代码中实现了operator->的操作符。

T* operator->() const { return ptr_; }

      因此,像这种以ptr结尾的类,最好都以非指针的方式来创建对象。以非指针方式创建对象还有另一个好处,就是当对象离开它所属的作用域时,会自动调用对象的析构函数进行自行销毁。这个也跟接下来要讲的scope单词相关。

     要想弄明白scope的含义,我们要学一个概念,叫RAII。这个概念如果单纯去看这个英文的全名(Resouce Acquisition Is Initialization),会让人莫名其妙,一头雾水。其实就是把一个指向堆内存空间的指针,存放在一个栈空间对象中,通过栈空间对象自动回收的功能,同时回收此指针指向的堆内存的方法。看个简单的例子吧,如果我们在一段作用域代码中创建了一个指针对象,当离开此作用域时,如果该指针没有传递给别的方法时,此时应该显式调用delete对指针进行释放。

A* a = new A();
delete a;
a = NULL;

     虽然对于经验丰富的C++程序员来说,忘记写最后两句的概率并不大,但是这些大量的释放内存的代码嵌在业务代码中也不能忽视。因此就有些人想了个高级的办法把指针的释放封装了起来。做法如下:

首先定义一个类:

class scoped_A{
private:
    A* a;
public:
    scoped_A(A* a){
        this.a = a;
    }
    ~scoped_A(){
        delete a;
        a = NULL;
    }
}

具体用法:


A* a = new A();
scoped_A(a) sa;

或者

scoped_A sa(new A());

    看到了吗?你可以理解为使用一个scoped_A的对象sa把指针对象a包住,当sa离开作用域自动调用scoped_A的析构函数,以达到自动释放a指针的目的。不得不说,这个是很妙的做法。这也是scope的含义,是指这个ptr对象是有范围的,离开了作用域,会自动回收。

    最后来说说ref,ref是引用的意思。在JAVA中,我们经常说一块内存会被多个变量引用,当这块内存没有被任何变量引用的时候,内存将会被垃圾回收机制所回收。在C++中,堆中的一块内存也会被多个指针变量同时引用(指向),有没有办法也像JAVA一样,在这块堆内存没有被任何指针引用时,自动回收呢?这个就要说到Webrtc中的RefCountedObject类了,关于这个类的解释可以看另一篇文章。要想彻底弄懂scoped_refptr,需要两篇文章放在一起看。

    总结:scoped_refptr是一个实现了在C++上自动回收对象内存的功能类。它能够在对象离开作用域时,通过引用计数器来判断是否回收堆中内存,实现了类似Java的垃圾回收的机制。

C++ 编程中,尤其是在使用 WebRTC 库时,类型转换错误是较为常见的问题之一。当遇到 `cannot convert rtc::scoped_refptr<MediaSource<CustomAudioSource>> to webrtc::AudioSourceInterface*` 错误时,这表明编译器无法将一个 `rtc::scoped_refptr<MediaSource<CustomAudioSource>>` 类型的对象隐式转换为 `webrtc::AudioSourceInterface*` 类型的指针。 ### 原因分析 1. **类型不匹配**:`rtc::scoped_refptr<MediaSource<CustomAudioSource>>` 是一个智能指针,指向的是 `MediaSource<CustomAudioSource>` 类型的对象,而 `webrtc::AudioSourceInterface*` 是一个原始指针,指向的是 `AudioSourceInterface` 类型的对象。如果 `MediaSource<CustomAudioSource>` 并没有继承自 `AudioSourceInterface` 或者没有提供相应的转换机制,则编译器无法进行自动转换。 2. **缺少转换操作符或构造函数**:如果目标类型(即 `AudioSourceInterface`)没有定义接受源类型(如 `MediaSource<CustomAudioSource>`)的构造函数,或者源类型没有定义到目标类型的转换操作符,则需要显式地进行类型转换。 3. **多态性不足**:如果 `MediaSource<CustomAudioSource>` 没有从 `AudioSourceInterface` 继承,或者即使继承了但没有正确实现虚函数表,则无法通过基类指针访问派生类对象的功能。 ### 解决方案 #### 1. 显式转换 如果你确定 `MediaSource<CustomAudioSource>` 实际上是从 `AudioSourceInterface` 继承的,并且你希望将其转换为基类指针,可以使用 `static_cast` 进行显式转换: ```cpp rtc::scoped_refptr<MediaSource<CustomAudioSource>> source = ...; webrtc::AudioSourceInterface* audio_source = static_cast<webrtc::AudioSourceInterface*>(source.get()); ``` 这里的关键在于 `source.get()` 返回的是 `MediaSource<CustomAudioSource>*` 类型的指针,而 `static_cast` 可以帮助我们将这个指针转换为 `AudioSourceInterface*` 类型的指针,前提是 `MediaSource<CustomAudioSource>` 确实是从 `AudioSourceInterface` 继承的。 #### 2. 使用接口方法 另一种方法是确保 `MediaSource<CustomAudioSource>` 提供了一个返回 `AudioSourceInterface*` 的方法,例如: ```cpp class MediaSource<CustomAudioSource> : public AudioSourceInterface { public: // ... AudioSourceInterface* GetInterface() { return this; } }; ``` 然后你可以这样使用: ```cpp rtc::scoped_refptr<MediaSource<CustomAudioSource>> source = ...; webrtc::AudioSourceInterface* audio_source = source->GetInterface(); ``` 这种方法的好处是你可以在 `GetInterface` 方法内部进行必要的类型检查或转换逻辑,确保转换的安全性和正确性。 #### 3. 检查继承关系 如果上述方法仍然不能解决问题,建议检查 `MediaSource<CustomAudioSource>` 是否确实是从 `AudioSourceInterface` 继承的。可以通过查看类定义来确认这一点: ```cpp template<> class MediaSource<CustomAudioSource> : public AudioSourceInterface { // ... }; ``` 如果发现 `MediaSource<CustomAudioSource>` 并没有正确继承自 `AudioSourceInterface`,则需要修改类定义以确保正确的继承关系。 ### 总结 处理此类类型转换错误时,关键是理解源类型和目标类型之间的关系,并确保它们之间存在有效的转换路径。无论是通过显式转换、接口方法还是调整继承关系,都需要根据具体情况选择合适的方法来解决问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值