简体   繁体   English

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

[英]Returning struct from a function, how can I check that it is initialized?

I have the following struct in C++: 我在C ++中有以下结构:

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

And I have the following function: 我有以下功能:

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" is a stl::map defined as follows: “routing_table”是一个stl :: map,定义如下:

map< unsigned long int, routing_entry > routing_table;

My question now is, when using the consultTable function, I want to check that the return value is actually initialized, some how like in Java pseudocode (because I come from the Java camp): 我现在的问题是,当使用consultTable函数时,我想检查返回值是否实际初始化,Java伪代码中有些类似(因为我来自Java阵营):

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

then checking if r == null 然后检查r == null

There are a few problems here. 这里有一些问题。 The most urgent may be what happens when the destination ID is not found. 最紧急的可能是找不到目的地ID时发生的情况。 Since you have no constructor on the routing_entry and you are not default initialising, it will have undefined values. 由于在routing_entry上没有构造函数,并且您没有默认初始化,因此它将具有未定义的值。

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

One way to handle this is to default initialise. 处理此问题的一种方法是默认初始化。 This works by instructing the compiler to fill the structure with zeros. 这通过指示编译器用零填充结构来工作。 This is kind of a trick borrowed from C but it works well here. 这是从C借来的一种技巧,但它在这里运作良好。

routing_entry route={0};

You mention you are coming from Java, unlike in Java, structure and class members are not 0 initialised, so you should really handle that somehow. 你提到你来自Java,与Java不同,结构和类成员不是初始化的,所以你应该以某种方式处理它。 Another way is to define a constructor: 另一种方法是定义构造函数:

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
};

Also note that in C++, the size of the integer and char members is not defined in bits. 另请注意,在C ++中,整数和char成员的大小不是以位为单位定义的。 The char type is 1 byte (but a byte is a not defined, but usually 8 bits). char类型是1个字节(但是一个字节未定义,但通常是8位)。 The longs are usually 4 bytes these days but can be some other value. 这些天的长度通常是4个字节,但可以是其他值。

Moving on to your consultTable , with the initialisation fixed: 继续使用您的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
}

One way to tell might be to check if the structure is still zeroed out. 一种说法可能是检查结构是否仍然归零。 I prefer to refactor to have the function return bool to indicate success. 我更喜欢重构函数返回bool来表示成功。 Additionally, I always typedef STL structures for simplicity, so I'll do that here: 另外,为了简单起见,我总是输入dede STL结构,所以我会在这里做:

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

Then we pass in a reference to the routing entry to populate. 然后我们传入对路由条目的引用来填充。 This can be more efficient for the compiler, but probably that is irrelevant here - anyway this is just one method. 这对编译器来说可能更有效,但这可能与此无关 - 无论如何这只是一种方法。

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;
}

You would call it like this: 你会这样称呼它:

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

The best way I've found for this is to use boost::optional , which is designed to solve exactly this issue. 我发现的最好的方法是使用boost :: optional ,它旨在解决这个问题。

Your function would look something like this:- 你的功能看起来像这样: -

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>()
}

And your calling code looks like 你的调用代码看起来像

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

Generally, the use of "out" parameters (passing a pointer - or reference - to an object which is then "filled in" by the called function is frowned on in C++. The approach that everything "returned" by a function is contained in the return value, and that no function parameters are modified can make code more readable and maintainable in the long term. 通常,使用“out”参数(将指针 - 或引用 - 传递给被调用函数随后“填充”的对象)在C ++中是不受欢迎的。函数“返回”的所有方法都包含在返回值,并且没有修改任何函数参数可以使代码在长期内更具可读性和可维护性。

This is typical solution for your problem: 这是您的问题的典型解决方案:

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;
}

Instead of a pointer you could use a reference; 您可以使用引用代替指针; it's a matter of style. 这是一种风格问题。

First note that in C++, unlike in Java, users can define value types. 首先请注意,在C ++中,与Java不同,用户可以定义值类型。 This means that there are 2^32 * 2^32 * 2^8 possible values for a routing_entry. 这意味着routing_entry有2 ^ 32 * 2 ^ 32 * 2 ^ 8个可能的值。 If you want, you can think of routing_entry as a 72-bit primitive type, although you have to be a bit careful with the analogy. 如果需要,您可以将routing_entry视为72位基元类型,尽管您必须对类比有点小心。

So, in Java route can be null, and there are 2^32 * 2^32 * 2^8 + 1 usefully different values for a routing_entry variable. 因此,在Java中, route可以为null,并且对于routing_entry变量有2 ^ 32 * 2 ^ 32 * 2 ^ 8 + 1有用的不同值。 In C++, it cannot be null. 在C ++中,它不能为null。 In Java "empty" might mean returning a null reference. 在Java中,“empty”可能意味着返回null引用。 In C++ only pointers can be null, and routing_entry is not a pointer type. 在C ++中,只有指针可以为null,而routing_entry不是指针类型。 So in your code in this case "empty" means "I have no idea what value this thing has, because I never initialized it or assigned to it". 所以在你的代码中,在这种情况下,“空”意味着“我不知道这个东西有什么价值,因为我从未初始化它或分配给它”。

In Java, a routing_entry object would be allocated on the heap. 在Java中,将在堆上分配routing_entry对象。 In C++ you don't want to do that unless you have to, because memory management in C++ takes effort. 在C ++中,除非必须,否则不希望这样做,因为C ++中的内存管理需要付出努力。

You have a few (good) options: 你有几个(好的)选择:

1) add a field to the routing entry, to indicate that it has been initialised. 1)在路由条目中添加一个字段,表明它已经初始化。 Chances are this won't make the struct any bigger, because of padding and alignment requirements of your implementation: 由于实现的填充和对齐要求,这可能不会使结构更大:

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
};

Why not use bool? 为什么不用bool? Because the standard helpfully allows sizeof(bool) != 1 . 因为标准有助于sizeof(bool) != 1 It's entirely possible that a bool is implemented as an int, especially if you have an oldish C++ compiler. 将bool实现为int是完全可能的,特别是如果你有一个旧的C ++编译器。 That would make your struct bigger. 这将使你的结构更大。

Then make sure the struct is inited with 0 values in your function, instead of whatever garbage was on the stack: 然后确保结构在函数中包含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
}

And make sure that all the entriess in the map have the initialized field set to non-zero. 并确保地图中的所有entriess都将初始化字段设置为非零。 Caller then checks initialized. 然后呼叫者检查初始化。

2) Use "magic" values of the existing fields as markers. 2)使用现有字段的“magic”值作为标记。

Suppose for the sake of argument that you never deal in routes with hopCount 0. Then as long as you 0-initialize as above, callers can check hopCount != 0. The max values of the types are also good flag values - since you're restricting your routes to 256 hops, chances are you won't do any harm by restricting them to 255 hops. 假设为了参数,你永远不会使用hopCount 0处理路由。然后只要你如上所述进行0初始化,调用者就可以检查hopCount!= 0.类型的最大值也是好的标志值 - 因为你'重新限制你的路线到256跳,你可能不会做任何伤害限制它们到255跳。 Rather than callers having to remember this, add a method to the struct: 而不是调用者必须记住这一点,向结构添加一个方法:

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; }
};

Then you'd initialise like this: 然后你会这样初始化:

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

or if you're worried what happens when you change the order or number of fields in future: 或者如果您担心将来更改字段的顺序或数量时会发生什么:

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

And the caller does: 并且来电者做:

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

3) Caller passes in a routing_entry by pointer or non-const reference. 3)调用者通过指针或非const引用传递routing_entry Callee fills the answer into that, and returns a value indicating whether it succeeded or not. Callee将答案填入其中,并返回一个值,表明它是否成功。 This is commonly called an "out param", because it sort of simulates the function returning a routing_entry and a bool. 这通常称为“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;
}

Caller does: 来电者:

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

By the way, when using a map your code as it is looks up the ID twice. 顺便说一句,当使用地图时,您的代码会查找ID两次。 You can avoid this as follows, although it's unlikely to make a noticeable difference to the performance of your app: 你可以按照以下方式避免这种情况,尽管它不太可能对你的应用程序的性能产生明显的影响:

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

Another way is to make your function return a status value (HRESULT or similar) indicating whether it was initialized, and pass the pointer to the struct as one of the parameters. 另一种方法是使函数返回一个状态值(HRESULT或类似值),指示它是否已初始化,并将指针作为参数之一传递给struct。

In C++, it's common to return a status indicating the error code (or 0 if success), but this of course depends on your programming habits. 在C ++中,通常返回指示错误代码的状态(如果成功则返回0),但这当然取决于您的编程习惯。

Simply passing a pointer and checking for null would work anyway. 无论如何,简单地传递指针并检查null都会有效。

As an alternative to the input - output parameter solution, you could follow Uncle Bobs advice and create a entry reader class. 作为输入 - 输出参数解决方案的替代方案,您可以遵循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
}

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.
}

G'day, 天儿真好,

Agreeing with most of what 1800 has to say, I'd be more inclined to make your function consultTable return a pointer to a routing_entry structure rather than a boolean. 同意1800的大部分内容,我更倾向于使你的函数consultTable返回一个指向routing_entry结构而不是布尔值的指针。

If the entry is found in the table, the function returns a pointer to a new routing_entry. 如果在表中找到该条目,则该函数返回指向新routing_entry的指针。 If it is not found, then it returns NULL. 如果未找到,则返回NULL。

BTW Good answer, 1800. BTW答案很好,1800。

HTH HTH

cheers, 干杯,

In your method 在你的方法

routing_entry Cnode_router_aodv::consultTable(unsigned int destinationID ) {

    routing_entry route;
    ...
    return route;
}

You are trying to return an automatic, ie the object is on the local stack frame, object. 您正在尝试返回自动,即对象位于本地堆栈框架对象上。 This will never do what you want it to do as this memory is not available when the function goes out of scope. 这将永远不会做你想要它做的事情,因为当函数超出范围时,这个内存不可用。

You will need to create the object, and then return the newly created object. 您将需要创建该对象,然后返回新创建的对象。 I suggest you consult Scott Meyers Effective C++ Third Edition, Item #21. 我建议你咨询Scott Meyers Effective C ++ Third Edition,Item#21。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如果我想使用该结构中的字段作为参数,如何从结构内部使用递归 function - How can I use a recursive function from inside a struct if I want to use a field from that struct as a parameter 如何将参数从结构正确传递给函数? - How can I pass parameters from a struct to a function correctly? 如何编写模板类中声明的函数定义,返回指向struct对象的指针? - How can I write a definition of function declared in template class returning pointer to struct object? 无法从函数返回结构 - Trouble returning a struct from a function 可以这样初始化结构吗? - Can struct be Initialized this way? "如何检查编译器初始化大结构中的所有字段" - How to check all fields in a big struct are initialized by compiler 返回指向结构的指针,如何自动释放内存? - returning pointer to a struct, how can I free memory automatically? 如何避免每次调用C ++函数时都重新初始化变量? - How can I keep variables from being re-initialized every time I call a C++ function? 从函数返回值:reference vs struct - Returning values from a function : reference vs struct 从函数返回时,如何检查 dynamic_cast 是否成功? - How do I check a dynamic_cast is successful when returning it from a function?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM