NS3 Object模型內容翻譯

weixin_34236497發表於2017-06-25

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<WifiNetDevice> device = CreateObject<WifiNetDevice> ();

不要使用 operator new 來建立這類物件。應該使用 CreateObject() 來建立。

對於由 class RefCountBase 派生的物件,或其他支援智慧指標類用法的物件
(特別地,比如 ns-3 Packet class),建議使用模板化的 helper function:

Ptr<B> b = Create<B> ();

這是一個對 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> node)
{
Ptr<Ipv4L3Protocol> ipv4 = CreateObject<Ipv4L3Protocol> ();
ipv4->SetNode (node);
node->AggregateObject (ipv4);
Ptr<Ipv4Impl> ipv4Impl = CreateObject<Ipv4Impl> ();
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> ipv4 = m_node->GetObject<Ipv4> ();

如果實際上沒有 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<T> 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> object = factory.Create ();
factory.Set ("SystemLoss", DoubleValue (3.0));
// Create another object with a different SystemLoss 再次建立一個對戲那個
Ptr<Object> object = factory.Create ();

1.6.8 Downcasting

有一個經常出現的問題:”假設我有一個基類的指標(Ptr),該指標指向一個對
象,如果我想要派生類的指標,那麼我應該進行 downcast(通過 C++的動態類
型轉換)來獲得派生的指標還是應該使用物件聚合系統進行 GetObject<> () 來
找到一個 Ptr,該指標指向子類 API 的介面”?

這個問題的答案是:在多數情況下,兩種技術都行的通。ns-3 提供一個模板化
的函式,該函式使得物件動態型別轉換的語法更加友好:

template <typename T1, typename T2>
Ptr<T1>
DynamicCast (Ptr<T2> const&p)
{
return Ptr<T1> (dynamic_cast<T1 *> (PeekPointer (p)));
}

當程式設計師有一個基類的指標,想和一個子類的指標進行測試時,DynamicCast
行的通。當尋找被聚合的不同物件時, GetObject 行的通。但對於子類, GetObject
也行的通,和 DynamicCast 一樣。如果不確定,那麼程式設計師應該使用 GetObject,
因為他在所有情況下都適用。如果程式設計師知道所考慮的物件的類層次結構,使用
DynamicCast 更加直接。

相關文章