2009-06-19 43 views
4

我在C++下面的結構:從函數返回結構體,如何檢查它是否已初始化?

struct routing_entry { 
     unsigned long destSeq; // 32 bits 
     unsigned long nextHop // 32 bits 
     unsigned char hopCount; // 8 bits 
}; 

我具有以下功能:

routing_entry Cnode_router_aodv::consultTable(unsigned int destinationID) {  
    routing_entry route; 

    if (routing_table.find(destinationID) != routing_table.end()) 
     route = routing_table[destinationID]; 

    return route; // will be "empty" if not found 
} 

「routing_table」 是STL ::定義爲地圖如下:

map< unsigned long int, routing_entry > routing_table; 

現在我的問題是,當使用consultTable函數時,我想檢查返回值是否被實際初始化,有些在Java僞代碼中很像(因爲我來自Java陣營):

Route consultTable(int id) { 
    Route r = table.find(id); 
    return r; 
} 

然後檢查如果r == NULL

+0

謝謝大家,我收到了很多非常有幫助的評論,他們真的打開了我的眼睛。我是C++的新手,但我堅持使用它,因爲我正在爲我的主人使用遺留代碼。 最後,我必須選擇一個答案,但我贊成所有那些啓發了我的答案。再次感謝:) – sabbour 2009-06-19 11:44:37

回答

15

這裏有幾個問題。最緊急的可能是當沒有找到目的地ID時發生的情況。由於routing_entry上沒有構造函數,因此您不是默認初始化,它將具有未定義的值。

// the data inside route is undefined at this point 
routing_entry route; 

處理此問題的一種方法是默認初始化。這通過指示編譯器用零填充結構來工作。這是從C借用的一種技巧,但在這裏效果很好。

routing_entry route={0}; 

你提到你是從Java來,不像在Java中,結構和類成員不是0初始化,所以你應該處理這個不知何故。另一種方法是定義構造:

struct routing_entry 
{ 
    routing_entry() 
    : destSeq(0) 
    , nextHop(0) 
    , hopCount(0) 
    { } 

      unsigned long destSeq; // 32 bits 
      unsigned long nextHop; // 32 bits 
      unsigned char hopCount; // 8 bits 
}; 

另外請注意,在C++中,整數和炭部件的尺寸沒有位定義。 char類型是1個字節(但是一個字節是未定義的,但通常是8位)。多頭通常是4個字節,但可以是其他值。

移動到您的consultTable,有固定的初始化:

routing_entry Cnode_router_aodv::consultTable(unsigned int destinationID) 
{  
    routing_entry route={0}; 

    if (routing_table.find(destinationID) != routing_table.end()) 
     route = routing_table[destinationID]; 

    return route; // will be "empty" if not found 
} 

一種方式告訴可能是檢查結構仍以歸零。我更願意重構該函數以返回bool以指示成功。此外,我總是簡單的typedef STL結構,所以我會在這裏這樣做:

typedef map< unsigned long int, routing_entry > RoutingTable; 
RoutingTable routing_table; 

然後我們傳遞的路由條目來填充參考。這對編譯器來說可能更有效率,但這可能與此無關 - 無論如何,這只是一種方法。

bool Cnode_router_aodv::consultTable(unsigned int destinationID, routing_entry &entry) 
{ 
    RoutingTable::const_iterator iter=routing_table.find(destinationID); 
    if (iter==routing_table.end()) 
    return false; 
    entry=iter->second; 
    return true; 
} 

你會這樣稱呼它:

routing_entry entry={0}; 
if (consultTable(id, entry)) 
{ 
    // do something with entry 
} 
+0

這看起來不錯。另一種方法是,如果你真的想返回routing_entry,那麼你的結構體上有一個名爲empty()的函數,或者返回你的成員值是否與默認值相同的東西。 – Salgar 2009-06-19 10:45:32

+0

這真的很有用:)你睜大眼睛看了很多東西! – sabbour 2009-06-19 11:23:25

+0

+1:小點:初始程序中不需要0:routing_entry entry = {};很好。 – 2009-06-19 11:45:32

0

另一種方法是使你的函數返回指示是否被初始化的狀態值(HRESULT或類似),並通過該指針結構作爲參數之一。

在C++中,返回指示錯誤代碼的狀態是常見的(如果成功則返回0),但這當然取決於您的編程習慣。

只要傳遞一個指針並檢查null就可以工作。

2

這是你的問題的典型解決方案:

bool Cnode_router_aodv::consultTable(unsigned int destinationID, 
            routing_entry* route) {  
    if (routing_table.find(destinationID) != routing_table.end()) { 
    *route = routing_table[destinationID]; 
    return true; 
    } 
    return false; 
} 

相反,你可以使用一個參考指針的;這是一個風格問題。

0

作爲輸入輸出參數解決方案的替代方案,您可以按照Bob叔叔的建議創建一個條目閱讀器類。

typedef map< unsigned long int, routing_entry > routing_table_type; 
routing_table_type routing_table; 


//Is valid as long as the entry is not removed from the map 
class routing_entry_reader 
{ 
    const routing_table_type::const_iterator routing_table_entry; 
    const routing_table_type& routing_table; 

public: 
    routing_entry_reader(const routing_table_type& routing_table, int destination_id) 
    : routing_table(routing_table), 
     routing_table_entry(routing_table.find(destination_id)) { 
    } 

    bool contains_entry() const { 
     return routing_table_entry!=routing_table.end(); 
    } 

    const routing_entry& entryByRef() const { 
     assert(contains_entry()); 
     return routing_table_entry->second; 
    } 
}; 


routing_entry_reader entry_reader(routing_table, destination_id); 
if(entry_reader.contains_entry()) 
{ 
    // read the values from the entry 
} 
0

shared_ptr<routing_entry> Cnode_router_aodv::consultTable(unsigned int destinationID) {  
    shared_ptr<routing_entry> route; 

    if (routing_table.find(destinationID) != routing_table.end()) 
    route.reset(new routing_entry(routing_table[destinationID])); 

    return route; // will be "empty" if not found 
} 

// using 
void Cnode_router_aodv::test() 
{ 
    shared_ptr<routing_entry> r = consultTable(some_value); 
    if (r != 0) { 
    // do something with r 
    } 
    // r will be freed automatically when leaving the scope. 
} 

3

我發現這種情況的最好方法是使用boost::optional,其目的是解決正是這個問題。

你的函數看起來是這樣的: -

boost::optional<routing_entry> consultTable(unsigned int destinationID) 
{  
    if (routing_table.find(destinationID) != routing_table.end()) 
    return routing_table[destinationID]; 
    else 
    return boost::optional<routing_entry>() 
} 

而且調用代碼看起來像

boost::optional<routing_entry> route = consultTable(42); 
if (route) 
    doSomethingWith(route.get()) 
else 
    report("consultTable failed to locate 42"); 

一般來說,採用「走出去」的參數(傳遞指針 - 或者參考 - 到被調用函數「填充」的對象在C++中被壓縮了,函數返回的所有東西都包含在返回值中,並且沒有修改函數參數可以使代碼更具可讀性,長期可維護。

+0

不錯,我也喜歡! – sabbour 2009-06-19 11:26:37

0

天兒真好,

與大多數的什麼1800不得不說達成一致,我會更傾向於讓你的函數consultTable返回一個指針到routing_entry結構,而不是一個布爾值。

如果在表中找到該條目,該函數將返回一個指向新的routing_entry的指針。如果找不到,則返回NULL。

BTW很好的回答,1800

HTH

歡呼聲,

1

首先要注意在C++中,不像在Java中,用戶可以定義值類型。這意味着對於routing_entry有2^32 * 2^32 * 2^8個可能的值。如果你願意,你可以將routing_entry想象成一個72位的原始類型,儘管你必須小心這個類比。

因此,在Java中route可以爲空,並且對於routing_entry變量有2^32 * 2^32 * 2^8 + 1有用的不同值。在C++中,它不能爲空。在Java中「空」可能意味着返回一個空引用。在C++中,只有指針可以爲空,並且routing_entry不是指針類型。因此,在這種情況下,你的代碼中的「空」意味着「我不知道這個東西有什麼價值,因爲我從來沒有初始化它或分配給它」。

在Java中,將在堆上分配一個routing_entry對象。在C++中,除非必須這麼做,否則不需要這樣做,因爲C++中的內存管理需要付出努力。

你有幾個(好)選項:

1)字段添加到路由表項,以表明它已被初始化。可能這不會讓的,因爲你的執行填充和對齊需求結構的任何更大,:

struct routing_entry { 
    unsigned long destSeq; // 32 bits on Win32. Could be different. 
    unsigned long nextHop // 32 bits on Win32. Could be different. 
    unsigned char hopCount; // 8 bits on all modern CPUs. Could be different. 
    unsigned char initialized; // ditto 
}; 

爲什麼不使用布爾?因爲標準有用地允許sizeof(bool) != 1。 bool完全可以作爲int來實現,特別是如果你有一個古老的C++編譯器。這會讓你的結構更大。

然後確保結構也是在你的函數inited 0值,是在棧上的,而不是什麼垃圾:

routing_entry Cnode_router_aodv::consultTable(unsigned int destinationID) {  
    routing_entry route = {}; 

    if (routing_table.find(destinationID) != routing_table.end()) 
     route = routing_table[destinationID]; 

    return route; // will be "empty" if not found 
} 

,並確保在地圖中所有entriess有初始化字段設置爲非零。呼叫者然後檢查初始化。

2)使用現有字段的「魔術」值作爲標記。

假設爲了論證,你從不處理hopCount 0的路由。然後只要你如上所述0初始化,調用者可以檢查hopCount!= 0。類型的最大值也是好標誌值 - 因爲你限制你的路由爲256跳,所以你不會將它們限制在255跳以內。而不必記住這個呼叫者的方法添加到結構:

struct routing_entry { 
    unsigned long destSeq; // 32 bits 
    unsigned long nextHop // 32 bits 
    unsigned char hopCount; // 8 bits 
    bool routeFound() { return hopCount != (unsigned char)-1; } 
}; 

然後你初始化像這樣:

routing_entry route = {0, 0, -1}; 

,或者如果你擔心,當你更改順序會發生什麼或將來字段數:

routing_entry route = {0}; 
route.hopCount = -1; 

,且調用者:

routing_entry myroute = consultTable(destID); 
if (myroute.routeFound()) { 
    // get on with it 
} else { 
    // destination unreachable. Look somewhere else. 
} 

3)調用者通過指針或非const引用傳入routing_entry。被調用者填寫答案,並返回一個值,表示它是否成功。這通常被稱爲「外部參數」,因爲它有點模擬返回一個布爾型的函數返回routing_entry

bool consultTable(unsigned int destinationID, routing_entry &route) {  
    if (routing_table.find(destinationID) != routing_table.end()) { 
     route = routing_table[destinationID]; 
     return true; 
    } 
    return false; 
} 

調用程序:

routing_entry route; 
if (consultTable(destID, route)) { 
    // route found 
} else { 
    // destination unreachable 
} 

順便說一句,使用的地圖,當你的代碼,因爲它是查找該ID兩次。您可避免這種 如下,雖然它不可能使一個明顯的區別,以您的應用程序的性能:

map< unsigned long int, routing_entry >::iterator it = 
    routing_table.find(destinationID); 
if (it != routing_table.end()) route = *it; 
0

在你的方法

routing_entry Cnode_router_aodv::consultTable(unsigned int destinationID) { 

    routing_entry route; 
    ... 
    return route; 
} 

您正在試圖返回一個自動的,即對象是本地堆棧幀,對象。這將永遠不會做你想做的事,因爲這個內存在函數超出範圍時不可用。

您將需要創建該對象,然後返回新創建的對象。我建議你參考Scott Meyers Effective C++第三版,Item#21。