函数传递结构或对象类型的参数时,优先采用引用传参的方式代替指针传参的方式
【说明】 C++语言中函数参数以传址的方式传递,除了支持指针传参外,相对于C语言而言,还支持引用传参的方式;相对于指针传参而言,引用传参可以传参执行效率相同的前提下,避免指针传参可能伴随的空指针检查,即在不降低安全性的前提下,减少可能的冗余指令数,因此优先采用引用传参的方式。
实际编码过程中,往往存在对数据的多层传址方式(即指针传参)使用,这种场景下可以考虑,对指针进行合法性判断后,将指针变量解引用,然后在内部进行引用传参。
【原理】 从C++编译器的底层实现分析,采用引用方式传参,与指针传参一样,均采用指针实现的方式免对象拷贝。如下,以结构对象成员操作函数为例:
struct CompValue {
int32_t x0;
int32_t x1;
int32_t x2;
};
int32_t DemoFuncByValue(CompValue input1, CompValue input2, CompValue &output)
{
output.x0 = input1.x0 + input2.x0;
output.x1 = input1.x1 + input2.x1;
output.x2 = input1.x2 + input2.x2;
return 0;
}
int32_t DemoFuncByRef(CompValue &input1, CompValue &input2, CompValue &output)
{
output.x0 = input1.x0 + input2.x0;
output.x1 = input1.x1 + input2.x1;
output.x2 = input1.x2 + input2.x2;
return 0;
}
int32_t DemoFuncByPointer(CompValue *input1, CompValue *input2, CompValue &output)
{
output.x0 = input1->x0 + input2->x0;
output.x1 = input1->x1 + input2->x1;
output.x2 = input1->x2 + input2->x2;
return 0;
}
int main(){
CompValue a{1,2,3};
CompValue b{4,5,6};
CompValue m{0,0,0};
DemoFuncByValue(a, b, m);
DemoFuncByRef(a, b, m);
DemoFuncByPointer(&a, &b, m);
return 0;
}
分析上述三个函数DemoFuncByValue、DemoFuncByRef、DemoFuncByPointer的调用过程,能够看到其反汇编代码如下所示,DemoFuncByValue(值传递)的调用开销最大,而DemoFuncByRef(引用传参)与DemoFuncByPointer(指针传参)的调用开销相同。
DemoFuncByValue(CompValue, CompValue, CompValue&):
sub sp, sp, #8
str lr, [sp, #-4]!
sub sp, sp, #20
add ip, sp, #16
stmdb ip, {r0, r1, r2}
ldr r2, [sp, #32]
ldr r0, [sp, #8]
ldr r1, [sp, #12]
ldr lr, [sp, #36]
add r0, r0, r2
ldr r2, [sp, #40]
add r1, r1, lr
stmib r2, {r0, r1}
mov r0, #0
ldr ip, [sp, #4]
str r3, [sp, #28]
add ip, ip, r3
str ip, [r2]
add sp, sp, #20
ldr lr, [sp], #4
add sp, sp, #8
bx lr
DemoFuncByRef(CompValue&, CompValue&, CompValue&):
ldr ip, [r0, #4]
ldr r3, [r0, #8]
push {r4, lr}
ldr r4, [r1]
ldr lr, [r0]
add r0, lr, r4
ldmib r1, {r4, lr}
add r1, ip, r4
add r3, r3, lr
stm r2, {r0, r1, r3}
mov r0, #0
pop {r4, pc}
DemoFuncByPointer(CompValue*, CompValue*, CompValue&):
ldr ip, [r0, #4]
ldr r3, [r0, #8]
push {r4, lr}
ldr r4, [r1]
ldr lr, [r0]
add r0, lr, r4
ldmib r1, {r4, lr}
add r1, ip, r4
add r3, r3, lr
stm r2, {r0, r1, r3}
mov r0, #0
pop {r4, pc}
main:
mov r0, #0
bx lr
【注意事项】 不涉及
【案例】
优化前:
int32_t MsgDataProc(ModuleMsgHeader *msg)
{
if (msg == nullptr) {
// error handling!
}
// main process
...
return 0;
}
说明:上述模块的消息分发接口MsgDataProc是一个通用函数,采用指针传参的方式,函数体需要对msg进行空指针保护,由于消息结构大概率是非空的,所以大部分场景下该空指针检查不会报错,但是考虑到代码的安全性,空指针检查不能删除。
优化后:
int32_t MsgDataProc(ModuleMsgHeader &msg)
{
// main process
...
return 0;
}
说明:结合上下文修改,将该函数的修改为引用传参的方式,避免了空指针检查,降低执行的指令数。
父主题: C++语言