为什么将结构传递给函数而不是单独的参数?
Why pass structure to function instead of separate parameters?
假设您这样调用一个函数:
someFunc( some_int, some_float, false, "whatever text");
这看起来不太好,但如果我通过结构传递它们/class,它看起来也不会好多少,因为我会像我一样即时构建结构带有函数参数。
someFunc( FuncParameters( some_int, some_float, false, "whatever text"));
即使结构在其定义中有参数名称,我在调用它的构造函数时也看不到这些名称。
我可以这样做:
FuncParameters func_parameters;
func_parameters.some_int_data = some_int;
func_parameters.some_float_data = some_float;
func_parameters.some_text_data = "whatever text";
someFunc(func_parameters);
但是如果我像上面那样忘记了布尔数据,那么就没有什么可以抱怨的了。
那么为什么人们说“如果参数数量超过 X,总是在结构中传递参数?我错过了什么?
使用结构而不是单独的参数有几个优点:
- 你表示数据是相关的。通过适当地命名结构,您可以为这个关系命名。
- 对相关数据进行分组的结构有机会在您的代码中的其他地方重复使用。
- 修改结构(add/remove 成员)后,无需调整参数列表。如果您通过多个函数进行调用,则尤其如此,其中结构对象只是通过。
- 调用站点的命名成员(
soldier.speed = 3.2;
)比简单传递3.2
更清晰。这更适用于 bool 文字和函数调用,例如 CreateSoldier(3.2, true, false, true)
.
还有一些注意事项:
- 传递结构的方式(按值或指针)会影响复制的数据量,以及是否在运行时有额外的取消引用。
- 对于简单的代码,创建一个仅将其传递给函数的结构实际上会增加代码的冗长程度并降低代码的可读性。
- 只有在 C99 中你有 compound literals。对于旧版本的 C,无法传递临时对象,您需要显式声明一个变量。
- 如您所见,设计良好的函数可以强制初始化所有参数;但是您在调用站点看不到参数的名称。对于不带很多参数的函数,可以通过适当地命名函数来缓解这种情况(例如
SetSpeed(&soldier, 3.2)
)。
我认为一个主要原因是代码的可读性。您可以阅读 CppCoreGuidelines
Keep the number of function arguments low
Reason Having many arguments opens opportunities for confusion. Passing lots of arguments is often costly compared to alternatives.
...
Grouping arguments into "bundles" is a general technique to reduce the
number of arguments and to increase the opportunities for checking.
Example
void f(int* some_ints, int some_ints_length); // BAD: C style, unsafe
versus
void f(gsl::span<int> some_ints); // GOOD: safe, bounds-checked
以你为例
someFunc(10,20,true,"foo");
比
更令人困惑
struct SomeFuncParameters
{
int max_iterations = 100;
float epsilon = 1.e-6;
bool reinit = true;
std::string name = "";
};
void someFunc(const SomeFuncParameters& parameters) {}
int main()
{
// Variation 1
//
someFunc(SomeFuncParameters());
// Variation 2
//
someFunc(SomeFuncParameters{.max_iterations = 10,
.epsilon = 1e-4,
.reinit = false,
.name = "my name"});
// Variation 3
//
SomeFuncParameters someFuncParameters;
someFuncParameters.epsilon = 1e-10;
someFunc(someFuncParameters);
}
即使您必须编写更长的代码。
另请注意,当您使用结构体存储参数时,您可以轻松定义参数默认值。如果您想添加或删除一些参数作为调用站点,您也可以修改更少的代码:
someFunc(SomeFuncParameters());
someFunc(someFuncParameters);
不会受到影响。
我还为 named optional arguments 编写了一个小型 C++17 库,您可能会感兴趣。
假设您这样调用一个函数:
someFunc( some_int, some_float, false, "whatever text");
这看起来不太好,但如果我通过结构传递它们/class,它看起来也不会好多少,因为我会像我一样即时构建结构带有函数参数。
someFunc( FuncParameters( some_int, some_float, false, "whatever text"));
即使结构在其定义中有参数名称,我在调用它的构造函数时也看不到这些名称。
我可以这样做:
FuncParameters func_parameters;
func_parameters.some_int_data = some_int;
func_parameters.some_float_data = some_float;
func_parameters.some_text_data = "whatever text";
someFunc(func_parameters);
但是如果我像上面那样忘记了布尔数据,那么就没有什么可以抱怨的了。
那么为什么人们说“如果参数数量超过 X,总是在结构中传递参数?我错过了什么?
使用结构而不是单独的参数有几个优点:
- 你表示数据是相关的。通过适当地命名结构,您可以为这个关系命名。
- 对相关数据进行分组的结构有机会在您的代码中的其他地方重复使用。
- 修改结构(add/remove 成员)后,无需调整参数列表。如果您通过多个函数进行调用,则尤其如此,其中结构对象只是通过。
- 调用站点的命名成员(
soldier.speed = 3.2;
)比简单传递3.2
更清晰。这更适用于 bool 文字和函数调用,例如CreateSoldier(3.2, true, false, true)
.
还有一些注意事项:
- 传递结构的方式(按值或指针)会影响复制的数据量,以及是否在运行时有额外的取消引用。
- 对于简单的代码,创建一个仅将其传递给函数的结构实际上会增加代码的冗长程度并降低代码的可读性。
- 只有在 C99 中你有 compound literals。对于旧版本的 C,无法传递临时对象,您需要显式声明一个变量。
- 如您所见,设计良好的函数可以强制初始化所有参数;但是您在调用站点看不到参数的名称。对于不带很多参数的函数,可以通过适当地命名函数来缓解这种情况(例如
SetSpeed(&soldier, 3.2)
)。
我认为一个主要原因是代码的可读性。您可以阅读 CppCoreGuidelines
Keep the number of function arguments low
Reason Having many arguments opens opportunities for confusion. Passing lots of arguments is often costly compared to alternatives.
...
Grouping arguments into "bundles" is a general technique to reduce the number of arguments and to increase the opportunities for checking.
Example
void f(int* some_ints, int some_ints_length); // BAD: C style, unsafe
versus
void f(gsl::span<int> some_ints); // GOOD: safe, bounds-checked
以你为例
someFunc(10,20,true,"foo");
比
更令人困惑struct SomeFuncParameters
{
int max_iterations = 100;
float epsilon = 1.e-6;
bool reinit = true;
std::string name = "";
};
void someFunc(const SomeFuncParameters& parameters) {}
int main()
{
// Variation 1
//
someFunc(SomeFuncParameters());
// Variation 2
//
someFunc(SomeFuncParameters{.max_iterations = 10,
.epsilon = 1e-4,
.reinit = false,
.name = "my name"});
// Variation 3
//
SomeFuncParameters someFuncParameters;
someFuncParameters.epsilon = 1e-10;
someFunc(someFuncParameters);
}
即使您必须编写更长的代码。
另请注意,当您使用结构体存储参数时,您可以轻松定义参数默认值。如果您想添加或删除一些参数作为调用站点,您也可以修改更少的代码:
someFunc(SomeFuncParameters());
someFunc(someFuncParameters);
不会受到影响。
我还为 named optional arguments 编写了一个小型 C++17 库,您可能会感兴趣。