C/C++函数传参时struct/class应该传引用还是传值?
一、什么是PoD数据类型?
具体可以以参考
链接1: 什么是PoD数据类型?
链接2: C++中PoD类型
二、怎么判断一个struct/class是否为PoD类型?
C++11中可以通过类模板进行判断,其新特性is_trivial可以用于判断一个struct/class是否为PoD类型。
定义如下:
template <typename T> struct std::is_trivial;
is_trivial结构成员函数:value的返回值可以指示T是否为PoD类型。
当T包含默认的构造函数,那么T为trivial类型数据,即为PoD类型时,value为true;其他情况下,value为false。(关于trivial与non-trivial与PoD的关系,可参考C++ trivial和non-trivial构造函数及POD类型)
三、struct/class作为函数形参时,该如何传值?
根据上文,我们知道如何判断一个struct/class是否为PoD类型?现在我们可以看看struct/class作为形参时,不同的传值类型在效率上的区别。
我们先定义一个结构体
typedef struct NewType{
int a;
long b;
double c;
long int d;
short int e;
long long f;
long double g;
uint8_t h;
//string st;
} tNewType;
很显然,该结构体为PoD类型,我们也可用is_trivial来判断。当我们加入string st (string 为non-PoD类型) 的时候,tNewType就是non-PoD类型,因为它包含自定义的构造函数,为non-trivial类型。
我们测试的思路:分别将tNewType作为函数形参进行值传递和引用传递,来看看其所花费的总时间的差异,以证明不同情况下,传递效率不同。
测试代码如下:
#include <bits/stdc++.h>
#include <sys/time.h>
using namespace std;
typedef struct NewType{
int a;
long b;
double c;
long int d;
short int e;
long long f;
long double g;
uint8_t h;
//string st;
} tNewType;
// 值传递
void Test1(tNewType nt1, tNewType nt2, tNewType nt3, tNewType nt4, tNewType nt5)
{
}
// 引用传递
void Test2(tNewType& nt1, tNewType& nt2, tNewType& nt3, tNewType& nt4, tNewType& nt5)
{
}
// 进行1000000次传参测试
int main()
{
long int n1 = 0, n2 = 0;
timeval t1, t2, t3, t4;
gettimeofday(&t1, nullptr);
while (n1++ < 1000000) {
tNewType nt;
Test1(nt, nt, nt, nt, nt);
}
gettimeofday(&t2, nullptr);
double valms = t2.tv_sec * 1000 + t2.tv_usec / 1000 - (t1.tv_sec * 1000 + t1.tv_usec / 1000);
printf("val: t2 - t1 = %lf ms.\n", valms);
gettimeofday(&t3, nullptr);
while (n2++ < 1000000) {
tNewType nt;
Test2(nt, nt, nt, nt, nt);
}
gettimeofday(&t4, nullptr);
double refms = t4.tv_sec * 1000 + t4.tv_usec / 1000 - (t3.tv_sec * 1000 + t3.tv_usec / 1000);
printf("ref: t4 - t3 = %lf ms.\n", refms);
if (is_trivial<tNewType>::value) {
cout << "tNewType is POD. Passed by val is equal to ref." << endl;
} else {
cout << "tNewType is non-POD. Passed by ref is better to val." << endl;
}
return 0;
}
当tNewType为PoD类型结构体时,其测试结果如下:
val: t2 - t1 = 0.000000 ms.
ref: t4 - t3 = 0.000000 ms.
tNewType is POD. Passed by val is equal to ref.
当tNewType为non-PoD类型结构体时,其测试结果如下:
val: t2 - t1 = 34.000000 ms.
ref: t4 - t3 = 0.000000 ms.
tNewType is non-POD. Passed by ref is better to val.
可见, 当struct/class为PoD类型的时候,值传递和引用传递效率基本相当,花费时间基本为0。而当struct/class为non-PoD类型时,引用传递效率明显高于值传递。
这是因为当struct/class为non-PoD类型时,值传递会调用struct/class本身或者其non-PoD成员的自定义构造函数或拷贝构造函数,导致额外的开销。
因此,一般struct/class作为函数参数时,我们尽量使用引用传递,最好还是const &。
参考:
[1]: 《Effective C++》
[2]: https://blog.csdn.net/kongkongkkk/article/details/77414410
[3]: https://www.cnblogs.com/tracylee/archive/2012/10/18/2730164.html
[4]: https://www.jianshu.com/p/191f713587cf
[5]: https://blog.csdn.net/a627088424/article/details/48595525
[6]: https://en.cppreference.com/w/cpp/types/is_trivial