团队C++代码走查小结(设计篇)
本文内容源于团队的每日代码走查活动,对照Clean Code基本原则和项目C++编码规范将项目代码中实际出现的一些问题进行提炼归纳,以便经验传播、避免问题反复出现。 PS:由于信息安全已对示例代码进行去业务处理。
物理设计
冗余的头文件
// #include "haed1andhaed2.h"
#include "haed1.h"
Haed1 haed;
所包含的头文件已经被包含在其他头文件中,无需重复包含。
范围过大的头文件
// #include "all.h"
#include "part.h"
Part part;
所包含的头文件范围大于实际使用的范围,需要进一步缩小依赖范围。
重复
逻辑上的重复
项目中存在大量类似但又不完全一样的代码,可以通过OO的多态或者函数式编程的方法来消除。 示例:
void FakeSystem::wait(string session, string key, const EventId& eventId)
{
reschedure();
try{
auto msg = RECV_MSG_QUEUE.pop(session, key, eventId);
instKey = msg.instKey();
session = msg.session();
}catch(const Exception e)
{
RECV_MSG_QUEUE.dump();
throw e;
}
}
void FakeSystem::wait(const EventId& eventId)
{
reschedure();
try{
auto msg = RECV_MSG_QUEUE.pop(eventId, *this);
instKey = msg.instKey();
session = msg.session();
}catch(const Exception e)
{
RECV_MSG_QUEUE.dump();
throw e;
}
}
可以尝试通过lambda表达式来消除重复的逻辑:
void FakeSystem::wait(string session, string key, const EventId& eventId)
{
auto pop = [=](){
return RECV_MSG_QUEUE.pop(session, key, eventId);
};
waitMsg(eventId, pop);
}
void FakeSystem::wait(const EventId& eventId)
{
auto pop = [=](){
return RECV_MSG_QUEUE.pop(eventId, *this);
};
waitMsg(eventId, asserter, pop);
}
void FakeSystem::waitMsg(const EventId& eventId, const std::function<Message()>& pop)
{
reschedure();
try{
auto msg = pop();
instKey = msg.instKey();
session = msg.session();
}catch(const Exception e)
{
RECV_MSG_QUEUE.dump();
throw e;
}
}
效率提升
不必要的对象赋值
CellData lmCellInfoInterface(WORD32 Id)
{
XXXData xxxData = XXXRepository::getInstance().getXXXInfo(Id);
return xxxData;
}
直接return即可。
右值引用
使用右值引用实现移动语义减少拷贝赋值。 示例:
XXXInfo xxxInfo(&info);
xxxInfo.update(val);
auto var = map.find(info.id);
DCM_ASSERT_TRUE(var == map.end());
map[info.id] = std::move(xxxInfo)
无序容器
传统的map和set是有序的容器,在插入元素时会对容器进行自动排序,在非排序的场景下使用无序容器unordered_map和unordered_set会更加高效。 示例:
DEF_INTERFACE(If, 0x170323)
{
Status save();
Status update(val);
Info delete(val);
Status accept(Visitor&) const;
private:
std::unordered_map<ID, Info> map;
}
传统的map的实现使用的是红黑树,而无序map则是使用散列表实现。
技巧
auto类型推导的使用
通过auto关键字的使用,隐藏不必要的冗长信息。
using ns::IdQueryRequest;
using ns::Id;
Status IdQueryRole::build(ns::Message& msg)
{
IdQueryRequest* req = msg.id_query_request();
ASSERT_VALID_PTR(req);
Id* id = req->getId();
ASSERT_VALID_PTR(id);
id->set_id(ROLE(InfoField)->id());
id->set_val(ROLE(IdField)->value());
return SUCCESS;
}
~~using ns::IdQueryRequest;
using ns::Id;~~
Status IdQueryRole::build(ns::Message& msg)
{
auto req = msg.id_query_request();
ASSERT_VALID_PTR(req);
auto id = req->getId();
ASSERT_VALID_PTR(id);
id->set_id(ROLE(InfoField)->id());
id->set_val(ROLE(IdField)->value());
return SUCCESS;
}
基于范围的for循环
C++11基于范围的for以统一、简洁的方式来遍历容器和数组。
const Message pop(const EventId& eventId, const FakeSystem& sys)
{
typedef std::list<Message>::iterator Iter;
for (Iter i=msgs.begin(); i != msgs.end(); ++i)
{
if ((i->messageId() == eventId) && (sys.expect(*i)))
{
Message msg = *i;
msgs.erase(i);
return msg;
}
}
onFail(string(), string(), eventId);
}
改为:
const Message pop(const EventId& eventId, const FakeSystem& sys)
{
for (auto& msg: msgs)
{
if (msg.messageId() == eventId) && (sys.expect(msg))
{
Message out = *msg;
msgs.erase(msg);
return out;
}
}
onFail(string(), string(), eventId);
}
或者结合lambda:
const Message pop(const EventId& eventId, const FakeSystem& sys)
{
auto msg = find_if(msgs.begin(), msgs.end(), [&](Message& msg){
return (msg.messageId() == eventId) && (sys.expect(msg));
});
if(out != msgs.end())
{
Message out = *msg;
msgs.erase(msg);
return out;
}
onFail(string(), string(), eventId);
}
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!