NS3 Object模型内容翻译

ns-3-manual.pdf 对象模型翻译

1.6 对象模型 Object model

ns-3 本质上是一个基于 C++对象的系统。对象可以像往常一样依照 C++的规则
被声明以及实例化。ns-3 还给传统的 C++对象添加了一些新的特色来提供更强
的功能。本章为读者介绍 ns-3 的对象模型。

本节描述针对 ns-3 的对象的 C++类设计。总的来说,已经使用的设计模式包括
经典的 object-oriented 设计(多态的接口和实现)、接口与实现相分离、非虚
拟的公共接口设计模式、对象聚合设施以及 reference counting for memory
management。尽管 ns-3 的设计与上述的任意一个都不严格一致,但熟悉类似
COM 或 Bonobo 等构件模型的人将能够识别 ns-3 对象聚合模型中的设计元素。

1.6.1 面向对象行为 Object-oriented behavior

一般而言,C++对象提供常见的面向对象功能(抽象、封装、继承以及多态),
这些功能是经典的面相对象设计的一部分。ns-3 对象使用了这些特性。例如:

class Address
{
public:
Address ();
Address (uint8_t type, const uint8_t *buffer, uint8_t len);
Address (const Address & address);
Address &operator = (const Address &address);
...
private:
uint8_t m_type;
uint8_t m_len;
...
};

1.6.2 对象的基类 Object base classes

ns-3 中有 3 个特殊的基类。由 3 个基类继承的类能够用特殊的特性来实例化对
象。这 3 个基类是:

class Object
class ObjectBase
class RefCountBase

没有要求 ns-3 的对象都继承自这 3 个类,
但对于具有特殊特性的类,是这样的。

由 class Object 派生的类具有如下特性。

    ns-3 的类型和属性系统(参看 Attributes)。
    对象聚合系统
    a smart-pointer reference counting system (class Ptr)

由 class ObjectBase 派生的类具有上述前两个特性,不具有 smart pointers。

由class RefCountBase 派生的类只具有 the smart-pointer reference counting system.

在实际中,ns-3 的开发者碰到最多的是上述 3 个中的 class Object。

1.6.3 内存管理和类 Ptr Memory management and class Ptr

C++程序中的内存管理是一个复杂的过程,并且经常被不正确地处理或者被不一
致地处理。以下介绍我们决定使用的一个引用计数设计。

所有使用引用计数的对象都维护一个内部引用值,根据该值来决定对象什么时候
可以安全地删除他自身。每当有接口获得对象的指针时,该对象的引用计数值就
增加 1,这个增加是通过调用 calling Ref() 完成的。当用户不再使用该指针时,
该指针的用户负责显式调用 Unref() 来解除对该指针的引用。当引用计数减到 0
时,对象被删除。

• 当用户代码通过创建对象从该对象获得指针或者通过 QueryInterface 获得指针时,没有必要增加引用计数值。
• 当用户代码从其他的源(比如,对指针进行复制)获得指针时,用户代码必须调用 Ref() 来增加引用计数值。
• 该对象的指针的所有用户都必须调用 Unref() 来释放引用。

通过使用下边描述的 reference counting smart pointer class,调用 Unref() 的
负担有了一些缓解。

通过底层 API,并想在堆上显式分配 non-reference-counted 对象的用户使用
new 操作符,用户负责删除这类对象。

Reference counting smart pointer (Ptr)

引用计数智能指针(Ptr)Reference counting smart pointer (Ptr)
因为始终调用 Ref() 和 Unref() 很麻烦,所以 ns-3 提供类似于
Boost::intrusive_ptr 的智能指针 class Ptr。该智能指针类假定底层类型提供一

对 Ref 和 Unref 方法,且该对方法增加和减少对象的内部引用计数值。
这种实现使得你能够像操纵普通指针一样操纵智能指针:可以将他和 0 比较、
将他和其他指针比较以及给他赋 0 值,等等。

通过 GetPointer 和 PeekPointer 方法有可能从智能指针中提取出裸指针。
如果你想把用 new 产生的对象存储到一个智能指针。为了避免内存泄漏,我们
建议你使用 CreateObject 模板函数来创建对象并将他存储到智能指针。这些函
数是很小的便利函数,他们的目标仅仅是使你少敲些键盘。

1.6.4 CreateObject 和 Create CreateObject and Create

C++的对象可以被静态地创建、动态地创建以及自动地创建。这在 ns-3 中同样
适用,但系统中的一些对象有一些附加的框架。特别地,引用计数的对象通常使
用模板化的 Create 或 CreateObject 方法被分配。

对于由 class Object 派生的对象:

Ptr device = CreateObject ();

不要使用 operator new 来创建这类对象。应该使用 CreateObject() 来创建。

对于由 class RefCountBase 派生的对象,或其他支持智能指针类用法的对象
(特别地,比如 ns-3 Packet class),建议使用模板化的 helper function:

Ptr b = Create ();

这是一个对 new 操作符的封装,他正确地处理了引用计数系统。

1.6.5 聚合 Aggregation

ns-3 的对象聚合系统很大程度上是由一个认识促成的,即 ns-2 中一个很普遍
的用法是通过继承和多态来扩展协议模型。例如,TCP 的特殊版本
RenoTcpAgent 是由类 TcpAgent 派生的,并对基类的函数进行覆盖(override)。
尽管如此,ns-2 模型中出现的两个问题是 downcasts 和“weak base class” 。

Downcasting 是指一个过程,即使用指向某个基类对象的指针并在程序运行时查
询该指针来获得类型信息,然后将该指针显式转换为子类的指针,以便子类的
API 能够使用。

Weak base class 是指当某个类无法被有效地重用(由他进行派
生)出现的问题,因为他缺少必要的功能,导致开发者不得不修改基类,这将导
致基类 API 的增生,某些 API 可能并不是对所有子类都在语义上正确。

ns-3 使用查询接口设计模式来避免这些问题。该设计基于 Component Object
Model 和 GNOME Bonobo 的基础。尽管现在替代构件的完全的二进制兼容性
还不被支持,但我们努力简化语法和对模型编写者的影响。

1.6.6 聚合的例子 Aggregation example

ns-3 中,class Node 是使用聚合的一个很好的例子。注意 ns-3 中没有类 Node
的派生类(比如类 InternetNode 等),而是将构件(各种协议)聚合到节点中。
我们来研究一些 Ipv4 协议是如何被加入节点的。

static void
AddIpv4Stack(Ptr node)
{
Ptr ipv4 = CreateObject ();
ipv4->SetNode (node);
node->AggregateObject (ipv4);
Ptr ipv4Impl = CreateObject ();
ipv4Impl->SetIpv4 (ipv4);
node->AggregateObject (ipv4Impl);
}

注意 Ipv4 协议是用 CreateObject() 创建的。接着 Ipv4 协议被聚合到节点中。
这样,基类 Node 就不需要被编辑来使得用户使用指向基类 Node 的指针来访问
Ipv4 接口;用户可以在程序运行时来向节点请求指向该节点的 Ipv4 接口的指针。
用户如何向节点提出请求在下一小节描述。

注意:将多于一个的同一类型的对象聚合到某个 ns3::object 是编程错误。所以,
如果想要存储一个节点的所有活动的 sockets,聚合是不可选的。

GetObject 的例子 GetObject example

GetObject 是一个获得安全 downcasting 的类型安全的方法,并且使得接口能够
在对象上被找到。

考虑一个节点的指针 m_node,该指针指向一个节点对象,且先前已经将 Ipv4
的实现聚合到了该节点。客户代码想要配置一个默认的路由。为了实现这点,必
须访问该节点内的一个对象,且该对象具有 IP 转发配置的接口。如下:

Ptr ipv4 = m_node->GetObject ();

如果实际上没有 Ipv4 的对象被聚合到该节点,那么该方法将返回 null。因此,
检查该函数调用的返回值是一个好习惯。如果成功,则用户可以使用 Ptr,该指
针指向先前被聚合到该节点的 Ipv4 对象。

另一个如何使用聚合的例子是给对象添加可选的模型。例如,一个现存的 Node
对象可以具有一个在运行时被聚合到该节点对象的“Energy Model”对象(不需要
对节点类进行修改和重新编译)。一个现存的模型(比如一个无线网络设备)可
以通过”GetObject”来获得该能量模型并表现地就像该接口是内建在 Node 对象
的底层或者该接口是在运行时被聚合到该节点的。而其他节点却不需要知道能量
模型的任何事情。
我们希望这样的编程模式可以大量减小开发者修改各种基类的必要。

1.6.7 Object factories

一个常见的用法例子是创建许多相似的配置对象。你可以重复调用CreateObject(),
但是在ns-3中也有一个工厂设计模式。它已经在“helper” API中已经大量使用了。
ObjectFactory类可以用来初始化对象,和配置这些对向的属性:

void SetTypeId (TypeId tid);
void Set (std::string name, const AttributeValue &value);
Ptr Create (void) const;

第一个方法允许你使用ns-3 TypedId系统来具体指定创建对象的类型。
第二个方法允许你设置要创建对象的属性。
第三个方法允许你使用工厂对象自己创建对象。

例如:

ObjectFactory factory;
// Make this factory create objects of type FriisPropagationLossModel
factory.SetTypeId ("ns3::FriisPropagationLossModel")
// Make this factory object change a default value of an attribute, for
// subsequently created objects
factory.Set ("SystemLoss", DoubleValue (2.0));
// Create one such object
Ptr object = factory.Create ();
factory.Set ("SystemLoss", DoubleValue (3.0));
// Create another object with a different SystemLoss 再次创建一个对戏那个
Ptr object = factory.Create ();

1.6.8 Downcasting

有一个经常出现的问题:”假设我有一个基类的指针(Ptr),该指针指向一个对
象,如果我想要派生类的指针,那么我应该进行 downcast(通过 C++的动态类
型转换)来获得派生的指针还是应该使用对象聚合系统进行 GetObject<> () 来
找到一个 Ptr,该指针指向子类 API 的接口”?

这个问题的答案是:在多数情况下,两种技术都行的通。ns-3 提供一个模板化
的函数,该函数使得对象动态类型转换的语法更加友好:

template 
Ptr
DynamicCast (Ptr const&p)
{
return Ptr (dynamic_cast (PeekPointer (p)));
}

当程序员有一个基类的指针,想和一个子类的指针进行测试时,DynamicCast
行的通。当寻找被聚合的不同对象时, GetObject 行的通。但对于子类, GetObject
也行的通,和 DynamicCast 一样。如果不确定,那么程序员应该使用 GetObject,
因为他在所有情况下都适用。如果程序员知道所考虑的对象的类层次结构,使用
DynamicCast 更加直接。

你可能感兴趣的:(NS3 Object模型内容翻译)