我的班级中有一个成员函数,如下所示:
int MyClass::m_Func(int& val);
其中,我做了一些操作并将结果放入val
. 并且根据操作的结果,我从函数返回了不同的值。就像,如果成功,0
如果发生任何错误,我会返回或其他值。
我的一位朋友告诉我,将变量的引用传递给成员函数不是一个好习惯。这是真的吗?如果是,为什么会这样?
我的班级中有一个成员函数,如下所示:
int MyClass::m_Func(int& val);
其中,我做了一些操作并将结果放入val
. 并且根据操作的结果,我从函数返回了不同的值。就像,如果成功,0
如果发生任何错误,我会返回或其他值。
我的一位朋友告诉我,将变量的引用传递给成员函数不是一个好习惯。这是真的吗?如果是,为什么会这样?
这种做法有点可疑的主要原因,以及为什么我通常建议不要这样做的主要原因是,用户在调用您的函数时,
int val;
obj.my_Func(val);
可能不知道 的值val
实际上已被修改。当函数只有一个参数并且除了错误代码之外没有返回值时,这可能或多或少很明显,但只要你有一个稍微复杂的函数调用,例如
int x,y,rad,len;
obj.calculate_coord(x,y,rad,len);
目的可能是计算rad
andlen
和,x
反之亦然y
,或者完全不同的东西。由于参数名称是由用户选择的,并且它们的顺序或类型不会提示哪些用作输入,哪些用作输出参数,这可能会导致误解,从而使代码更难维护并产生错误.
另一方面,使用 C++ 语法不仅可以正确返回值或错误代码,而且可以正确返回一个或多个值和错误代码的组合,这非常简单。
在最简单的情况下,您可以使用这样的函数(使用 C++11 语法):
std::tuple<int,int,int> my_Func(int input)
{
/* calculate.. */
return { error_code , val1 , val2 };
}
返回两个值和一个错误代码。毫无疑问,这input
是一个纯输入参数(按值传递),返回值很复杂,包括您可能想要返回的所有内容。
与一些程序员可能建议的相反,这通常不会导致大量不必要的复制,即使返回值大得多(因为返回值优化,并且在 C++11 中因为移动语义)。
可能会有例外,尤其是在 C++11 之前的版本中,即无法使用移动语义或由于某种原因返回值优化不适用的特殊情况。在某些情况下,代码分析显示 a) 函数被大量使用,并且 b) 无论出于何种原因,通过引用传递输出会更快。在这些情况下,通过引用传递输出参数可能是正确的技术。
当然,我上面所说的仅适用于使用引用作为输出参数,即作为向用户返回值的间接方式,特别是当函数修改其某些参数这一事实并不能通过其姓名。
通过引用传递可能有许多其他有效的原因,尤其是。通过常量引用。
我想,您的朋友实际上并没有抱怨将变量的引用传递给方法,而是抱怨您选择的错误报告技术(实际上,人们通常被鼓励通过引用传递参数,这比传递要安全得多通过指针)。在 C++ 中,通常使用异常来检查错误;您的代码有点 C 风格。所以而不是:
int res;
myInstance->m_Func(res);
if (res != 0)
// do sth
我宁愿写:
MyClass::m_Func()
{
if (some_condition)
throw std::exception("Error!");
}
// (...)
try
{
myInstance->m_Func();
}
catch (...)
{
// do sth
}
免责声明:对此,意见会大相径庭,因为这是风格、惯例等问题。
您所拥有的是所谓的“外参数”,即您的函数不会通过返回值传递其结果,而是通过参数。这主要是因为返回值被错误码(即成功指示符)占用。
在 C 等许多语言中,这是函数和错误处理的常见用法。在某些 C++ 应用程序中,由于某种原因无法使用异常,也可以采用这种风格。然而,在“纯”C++ 中,错误处理存在异常。通常,函数的任何参数都是输入参数,函数产生的任何值都通过返回值传递出去。如果要返回多个值,则这些值将捆绑在一个std::pair
或std::tuple
某个结构中。
在您的情况下使用异常可能过于苛刻,可能是因为不成功并不是真正的例外情况,而是函数的常见和预期结果。在这种情况下,返回一对值 - 一个成功指示符和一个结果值。将它们捆绑在一起一开始可能看起来很奇怪,但这是一个可行的解决方案,因为无论如何您都必须对它们进行评估。但请记住,在大多数情况下,如果函数无法执行其任务(即计算出有用的返回值),抛出异常会更简洁。