2009-06-19 90 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。