Fra*_*ank 41 c++ parameters null overloading function
我有一个处理给定向量的函数,但如果没有给出,也可以自己创建这样的向量.
我看到这种情况有两种设计选择,其中一个函数参数是可选的:
将它设为指针并NULL默认设置为:
void foo(int i, std::vector<int>* optional = NULL) {
if(optional == NULL){
optional = new std::vector<int>();
// fill vector with data
}
// process vector
}
Run Code Online (Sandbox Code Playgroud)
或者有两个带有重载名称的函数,其中一个省略了参数:
void foo(int i) {
std::vector<int> vec;
// fill vec with data
foo(i, vec);
}
void foo(int i, const std::vector<int>& optional) {
// process vector
}
Run Code Online (Sandbox Code Playgroud)
是否有理由选择一种解决方案而不是另一种?
我稍微偏爱第二个,因为我可以将向量作为const引用,因为它在提供时只能读取而不能写入.此外,界面看起来更干净(NULL不仅仅是一个黑客?).并且间接函数调用产生的性能差异可能会被优化掉.
然而,我经常在代码中看到第一个解决方案.除了程序员的懒惰之外,是否有令人信服的理由更喜欢它?
Joh*_*ing 42
我不会使用任何一种方法.
在这种情况下,foo()的目的似乎是处理一个向量.也就是说,foo()的工作是处理向量.
但是在foo()的第二个版本中,隐含地给出了第二个工作:创建向量.foo()版本1和foo()版本2之间的语义不一样.
而不是这样做,我会考虑只有一个foo()函数来处理一个向量,而另一个函数创建向量,如果你需要这样的东西.
例如:
void foo(int i, const std::vector<int>& optional) {
// process vector
}
std::vector<int>* makeVector() {
return new std::vector<int>;
}
Run Code Online (Sandbox Code Playgroud)
显然这些函数是微不足道的,如果所有makeVector()需要做的就是完成它的工作就完全是调用new,那么使用makeVector()函数可能没有意义.但我确信在你的实际情况下,这些函数比这里显示的更多,而我上面的代码说明了语义设计的基本方法:给一个函数做一个工作.
我上面提到的foo()函数的设计也说明了我个人在我的代码中使用的另一种基本方法,当涉及到设计接口时 - 包括函数签名,类等等.这就是:我相信一个好的接口是1)容易和直观正确使用,2)难以或不可能错误地使用.在foo()函数的情况下,我们隐含地说,根据我的设计,向量必须已经存在并且已经"准备好".通过设计foo()来获取引用而不是指针,调用者必须已经有一个向量是直观的,并且他们将很难传递一些不是现成的向量.
Leo*_*Hat 28
我绝对赞成重载方法的第二种方法.
第一种方法(可选参数)模糊了方法的定义,因为它不再具有明确定义的目的.这反过来又增加了代码的复杂性,使不熟悉代码的人更难理解代码.
使用第二种方法(重载方法),每种方法都有明确的目的.每种方法都具有良好的结构和凝聚力.一些额外的说明:
Dav*_*ann 21
虽然我确实理解许多人对默认参数和过载的抱怨,但似乎对这些功能提供的好处缺乏了解.
默认参数值:
首先,我想指出,在项目的初始设计中,如果设计得很好,默认情况应该几乎没有用.但是,默认情况下,最大的资产发挥作用的是现有项目和完善的API.我从事包含数百万现有代码行的项目,并且无需重新编写所有代码.因此,当您希望添加需要额外参数的新功能时; 新参数需要默认值.否则,您将破坏使用您项目的每个人.哪个对我个人没问题,但我怀疑你的公司或产品/ API的用户会喜欢在每次更新时重新编码他们的项目. 简单来说,默认值非常适合向后兼容! 这通常是您将在大API或现有项目中看到默认值的原因.
函数覆盖:函数覆盖 的好处是它们允许共享功能概念,但具有不同的选项/参数.但是,很多时候我看到函数覆盖延迟用于提供截然不同的功能,只是略有不同的参数.在这种情况下,它们应该各自具有与其特定功能相关的单独命名的功能(与OP的示例一样).
这些c/c ++的功能很好,并且在正确使用时效果很好.这可以说是大多数编程功能.当他们被滥用/滥用时,他们会导致问题.
免责声明:
我知道这个问题已经有几年了,但是由于今天(2012年)我的搜索结果中出现了这些答案,我觉得这需要进一步解决未来的读者问题.
C++中的引用不能为NULL,一个非常好的解决方案是使用Nullable模板.这会让你做的事情是ref.isNull()
在这里你可以使用这个:
template<class T>
class Nullable {
public:
Nullable() {
m_set = false;
}
explicit
Nullable(T value) {
m_value = value;
m_set = true;
}
Nullable(const Nullable &src) {
m_set = src.m_set;
if(m_set)
m_value = src.m_value;
}
Nullable & operator =(const Nullable &RHS) {
m_set = RHS.m_set;
if(m_set)
m_value = RHS.m_value;
return *this;
}
bool operator ==(const Nullable &RHS) const {
if(!m_set && !RHS.m_set)
return true;
if(m_set != RHS.m_set)
return false;
return m_value == RHS.m_value;
}
bool operator !=(const Nullable &RHS) const {
return !operator==(RHS);
}
bool GetSet() const {
return m_set;
}
const T &GetValue() const {
return m_value;
}
T GetValueDefault(const T &defaultValue) const {
if(m_set)
return m_value;
return defaultValue;
}
void SetValue(const T &value) {
m_value = value;
m_set = true;
}
void Clear()
{
m_set = false;
}
private:
T m_value;
bool m_set;
};
Run Code Online (Sandbox Code Playgroud)
现在你可以拥有
void foo(int i, Nullable<AnyClass> &optional = Nullable<AnyClass>()) {
//you can do
if(optional.isNull()) {
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
我同意,我会使用两个功能.基本上,您有两种不同的用例,因此有两种不同的实现是有意义的.
我发现我编写的C++代码越多,我的参数默认值就越少 - 如果该功能被弃用,我真的不会流泪,尽管我不得不重新编写一堆旧代码!