我在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
答案 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。