从函数返回结构,如何检查它是否已初始化?

时间:2009-06-19 09:52:10

标签: c++ null

我在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,定义如下:

map< unsigned long int, routing_entry > routing_table;

现在我的问题是,当使用consultTable函数时,我想检查返回值是否实际初始化,Java伪代码中的一些(因为我来自Java阵营):

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

然后检查r == null

9 个答案:

答案 0 :(得分:15)

这里有一些问题。最紧急的可能是找不到目的地ID时发生的情况。由于routing_entry上没有构造函数,并且您没有默认初始化,因此它将具有未定义的值。

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

处理此问题的一种方法是默认初始化。这通过指示编译器用零填充结构来工作。这是从C借用的一种技巧,但它在这里运作良好。

routing_entry route={0};

你提到你来自Java,与Java不同,结构和类成员不是初始化的,所以你应该以某种方式真正处理它。另一种方法是定义构造函数:

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成员的大小不是以位为单位定义的。 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来表示成功。另外,为了简单起见,我总是输入dede 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
}

答案 1 :(得分: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");

通常,使用“out”参数(将一个指针 - 或引用 - 传递给一个被调用函数“填充”的对象在C ++中是不受欢迎的。一切都由函数“返回”的方法包含在返回值中,并且没有修改任何函数参数可以使代码在长期内更易读和可维护。

答案 2 :(得分: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;
}

您可以使用引用代替指针;这是一种风格问题。

答案 3 :(得分:1)

首先请注意,在C ++中,与Java不同,用户可以定义值类型。这意味着routing_entry有2 ^ 32 * 2 ^ 32 * 2 ^ 8个可能的值。如果你愿意,你可以把routing_entry想象成一个72位的原始类型,虽然你必须对这个类比有点小心。

因此,在Java route中可以为null,并且对于routing_entry变量有2 ^ 32 * 2 ^ 32 * 2 ^ 8 + 1有用的不同值。在C ++中,它不能为null。在Java中,“empty”可能意味着返回null引用。在C ++中,只有指针可以为null,而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
};

为什么不使用bool?因为该标准有助于sizeof(bool) != 1。将bool实现为int是完全可能的,特别是如果你有一个旧的C ++编译器。这会使你的结构更大。

然后确保结构在函数中包含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)使用现有字段的“magic”值作为标记。

假设你为了参数而从未用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。 Callee将答案填入其中,并返回一个值,表明它是否成功。这通常被称为“out param”,因为它模拟了返回routing_entry bool的函数。

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;

答案 4 :(得分:0)

另一种方法是使函数返回一个状态值(HRESULT或类似值),指示它是否已初始化,并将指针作为参数之一传递给struct。

在C ++中,通常返回指示错误代码的状态(如果成功则返回0),但这当然取决于您的编程习惯。

无论如何,只需传递一个指针并检查null即可。

答案 5 :(得分:0)

作为输入输出参数解决方案的替代方案,您可以遵循Uncle Bobs建议并创建入门读者类。

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
}

答案 6 :(得分: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.
}

答案 7 :(得分:0)

天儿真好,

同意1800的大部分内容,我更倾向于使你的函数consultTable返回一个指向routing_entry结构而不是布尔值的指针。

如果在表中找到该条目,则该函数返回指向新routing_entry的指针。如果未找到,则返回NULL。

BTW答案很好,1800。

HTH

欢呼声,

答案 8 :(得分:0)

在你的方法中

routing_entry Cnode_router_aodv::consultTable(unsigned int destinationID ) {

    routing_entry route;
    ...
    return route;
}

您正在尝试返回自动,即对象位于本地堆栈帧对象上。这将永远不会做你想要它做的事情,因为当函数超出范围时,这个内存不可用。

您需要创建对象,然后返回新创建的对象。我建议你咨询Scott Meyers Effective C ++ Third Edition,Item#21。