【NS3】对象模型和智能指针

对象模型

理解程序设计的最佳切入点?——对象模型

对象模型的作用?

一、单个类的管理。(基类与子类)
尽管每个类表示的网络元素不同,但它们往往都具有某些相同的功能需求,如动态内存管理和属性配置等。没必要在每一个类中分别实现这些需求。因此需要一些顶层的基类来统一定义与实现这些共性特征。而子类只需关注自身专属特性即可。

二、多个类的管理。(关联各个类)
任何一个单一的类时无法完成网络模拟的。这些类需要被有机地关联起来,形成一个可以处理各种网络事件的独立主题。例如,一个网络结点需要整合应用程序、通信信道、网络设备和TCP/IP层协议等多个C++类才能与其他结点进行通信。这些类之间如何灵活、高效的关联,也是对象模型解决的问题之一。

如何实现上述目的?

定义三个基类:SimpleRefCountObjectBaseObject

SimpleRefCount:解决针对单个类的动态内存管理问题,智能指针的实现。

ObjectBase解决单个类需要配置属性和trace变量的需求。

Object类解决了多个类之间动态关联问题。
这种类之间的动态关联是通过一种叫做对象聚合的功能实现的。简单地说,对象聚合能够让一个Object对象在运行时动态地关联其他对象,而不是在编译时关联。

下图给出了上面三个基类的继承关系,以及它们各自的一些子类代表。箭头指向的类是基类。
在这里插入图片描述

智能指针

为什么使用智能指针?

传统的动态内存是从new运算符创建到被delete运算符销毁。尽管这种设计增加了内存管理的灵活度,但同时造成了两个严重的安全隐患。

内存泄漏:动态内存没有被及时释放
空悬指针:在尚有其他指针引用的情况下释放动态内存

智能指针如何解决这两个安全隐患的呢?

除了与常规指针有着相似的行为(拷贝、赋值等),更重要的是其能够自动释放那些没有指针指向的动态内存,从而避免了内存泄漏和空悬指针问题,提高了程序的安全性。

如何实现智能指针?

智能指针由PtrSimpleRefCount两个类模板组成。
其中,SimpleRefCount类是ns-3智能指针的内部实现。它定义了一个引用计数器来记录指向自身内存的指针数量。
Ptr类则定义了ns-3智能指针的外部接口

设计原理

Ptr使用范式

Ptr<类名>指针变量名

Ptr的实现

两部分:
第一部分负责保存原始指针和模拟原始指针操作(复制、赋值),这一部分通过Ptr实现;
第二部分负责记录所有指向所分配的对象内存的指针数量(引用计数器),这一部分通过SimpleRefCount实现。

下面的代码中,模板形参T(typename T)是Ptr指针指向对象的类名称。指向对象内存的原始指针m_ptr保存在Ptr类中。
SimpleRefCount类定义了一个引用计数器m_count变量。

这段代码还包括SimpleRefCount类的Ref()Unref()函数,分别负责计数器值的增减,当计数器变为0时,意味着没有任何指针指向该内存,这时,Unref()函数会调用删除函数销毁对象。
在这里插入图片描述
类模板

Ref()和Unref()函数怎么使用?

定义一个SimpleRefCount的子类MyTestPtr指针t1t2分别指向两个MyTest对象。

Ptr<MyTest>t1 = Create<MyTest>();
Ptr<MyTest>t2 = Create<MyTest>();

t2 = t1; //t1指向t2???

由于赋值操作使得MyTest_2获得了一个新的Ptr指针t1,因此t1在指向新内存之后立即调用MyTest_2Ref()函数,使其计数器加1,变为2。相反,MyTest_1对象在这个过程中失去了原来的Ptr指针t1,因此在t1指向新内存之前会调用MyTest_1Unref()函数,使其计数器减1。此时,MyTest_1的计数器会变为0,被销毁。

在这里插入图片描述

使用实例

  1. 初始化
Packet *p = NULL;
Ptr<Packet>ptr;
  1. 创建对象
//使用Packet()构造函数
Packet *p      = new Packet();
Ptr<Packet>ptr = Create<Packet>();

//使用Packet(uint32_t size)构造函数
Packet *p      = new Packet(100);
Ptr<Packet>ptr = Create<Packet>(100);
  1. 赋值操作
Packet *p        = new Packet();
Ptr<Packet>ptr_1 = p;
Ptr<Packet>ptr_2 = ptr; //???
  1. 指针运算
ptr->GetUid();    //->
(*ptr).GetUid();  //*

一个是指针指向的函数,一个是指针解引用之后的函数,但这两行最后都会执行函数,效果相同。

  1. 比较运算
if(ptr == ptr_1){}
if(ptr != ptr_1){}
if(ptr == p){}
if(ptr != p){}

等于和不等于符号可以用于两个Ptr指针之间,也可以用于一个Ptr指针和一个原始指针之间。

if(ptr < ptr_1){}
if(ptr <= ptr_1){}
if(ptr > ptr_1){}
if(ptr >= ptr_1){}
  1. 流插入
std::cout<<"address:"<<ptr<<std::endl;

输出Ptr指针中原始指针的地址。

  1. 拷贝
    Ptr指针的拷贝有两种:指针拷贝对象拷贝

指针拷贝:
被拷贝的和新的Ptr指针均指向同一个对象。

Packet *p = new Packet();
Ptr<Packet>ptr(p);
Ptr<Packet>ptr_1(ptr); //Packet对象计数器加1

对象拷贝:
使用的场景:不只需要一个新的指针,还需要拷贝出一个新的对象,开辟新的内存空间。这时候就需要用到Copy()函数。
使用比较少。

Ptr<Packet>ptr = Create<Packet>();
Ptr<Packet>ptr_1 = Copy(ptr);//ptr_1指向一个新创建的Packet对象
  1. 类型转换
    ns-3提供了DynamicCast()StaticConst()ConstCast()三个Ptr指针类型转换函数。
    注意:每次调用这些函数后,都需要检查其返回Ptr指针是否为空,确认转换是否成功
//DynamicCast
Ptr<NetDevice>pDev = Create<LoopbackNetDevice>();
Ptr<LoopbackNetDevice>pLoopDev = DynamicCast<LoopbackNetDevice>(pDev);
if(!pLoopDev )
	NS_LOG_UNCOND("DynamicCast failure");

//StaticCast
Ptr<LoopbackNetDevice>pLoopDev1 = StaticCast<LoopbackNetDevice>(pDev);
if(!pLoopDev1 )
	NS_LOG_UNCOND("StaticCast failure");

//ConstCast
Ptr<Packet const>ptr = Create<Packet>();
Ptr<Packet>ptr_1 = ConstCast<Packet>(ptr);
if(!ptr_1)
	NS_LOG_UNCOND("ConstCast failure");
  1. 获取原始指针
    获取原始指针有两个函数:PeekPointer()GetPointer()
    GetPointer()会对Ptr指针指向对象的计数器加1。相当于新创建了一个指针指向这个对象。使用极少。
    PeekPointer()则没有这个要求。
Ptr<Packet>ptr = Create<Packet>();
Packet *p = PeekPointer(ptr);

适用范围

Ptr的作用
提供了和原始指针相同的操作集,实现了内存的动态管理。

局限性:

  • 只能支持构造函数参数少于或等于7个的类。
  • 只能引用与SimpleRefCount子类对象。
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值