C++ 带有C++;建设者
使用默认参数的类构造函数是一种好的做法,还是应该使用单独的重载构造函数?例如:C++ 带有C++;建设者,c++,constructor,coding-style,overloading,C++,Constructor,Coding Style,Overloading,使用默认参数的类构造函数是一种好的做法,还是应该使用单独的重载构造函数?例如: // Use this... class foo { private: std::string name_; unsigned int age_; public: foo(const std::string& name = "", const unsigned int age = 0) : name_(name), age_(age) {
// Use this...
class foo
{
private:
std::string name_;
unsigned int age_;
public:
foo(const std::string& name = "", const unsigned int age = 0) :
name_(name),
age_(age)
{
...
}
};
// Or this?
class foo
{
private:
std::string name_;
unsigned int age_;
public:
foo() :
name_(""),
age_(0)
{
}
foo(const std::string& name, const unsigned int age) :
name_(name),
age_(age)
{
...
}
};
// Header
void doSomething() ;
void doSomething(int i) ;
// Source
void doSomething()
{
doSomething(25) ;
}
void doSomething(int i)
{
// Do something with i
}
这两个版本似乎都有效,例如:
foo f1;
foo f2("Name", 30);
你喜欢或推荐哪种风格?为什么?肯定是风格的问题。我更喜欢带有默认参数的构造函数,只要参数有意义。标准中的类也使用它们,这对它们有利
需要注意的一点是,如果除了一个参数外,所有参数都有默认值,则可以隐式转换该参数类型的类。查看更多信息。根据我的经验,默认参数在当时似乎很酷,让我的懒惰系数很开心,但后来我使用了这个类,当默认参数生效时,我感到惊讶。所以我真的认为这不是一个好主意;最好先有一个className::className(),然后是一个className::init(arglist)。只是为了维护性的边缘。< p>我会使用默认参数,特别是因为C++不允许你链构造函数(所以你必须复制初始化列表,并且可能每个副本都有更多)。 也就是说,有一些带有默认参数的gotcha,包括常量可以内联(从而成为类的二进制接口的一部分)这一事实。另一个需要注意的是,添加默认参数可能会将显式多参数构造函数变成隐式单参数构造函数:
class Vehicle {
public:
Vehicle(int wheels, std::string name = "Mini");
};
Vehicle x = 5; // this compiles just fine... did you really want it to?
出于这个原因,我将使用默认参数:您的示例假设ctor参数直接对应于成员变量。但如果情况并非如此,您必须在初始化对象之前处理参数,该怎么办。拥有一个通用的ctor是最好的选择。默认参数让我感到困扰的一件事是,您不能指定最后一个参数,而是使用默认值作为第一个参数。例如,在您的代码中,您不能创建一个没有名字但具有给定年龄的Foo(但是,如果我没记错的话,这在C++0x中是可能的,使用统一的构造语法)。有时候,这是有道理的,但也可能真的很尴尬
在我看来,没有经验法则。就我个人而言,我倾向于使用多个重载构造函数(或方法),除非只有最后一个参数需要默认值。如果用参数创建构造函数不好(正如许多人所认为的那样),那么用默认参数使其更糟。我最近开始认为ctor参数是不好的,因为您的ctor逻辑应该尽可能地最小。如果有人传入一个毫无意义的参数,您如何处理ctor中的错误处理?您可以抛出一个异常,这是一个坏消息,除非您的所有调用者都准备在try块中包装任何“新”调用,或者设置一些“is initialized”成员变量,这是一种肮脏的攻击 因此,确保传递到对象初始化阶段的参数的唯一方法是设置一个单独的initialize()方法,您可以在其中检查返回代码 使用默认参数不好有两个原因;首先,如果您想向ctor添加另一个参数,那么您必须将其放在开头并更改整个API。此外,大多数程序员习惯于通过实践中使用的方式来理解API——这对于组织内部使用的非公共API尤其如此,因为在组织内部可能不存在正式文档。当其他程序员看到大多数调用都不包含任何参数时,他们也会这样做,幸福地不知道默认参数强加给他们的默认行为
另外,值得注意的是,避免使用ctor参数(除非绝对必要)和。此讨论既适用于构造函数,也适用于方法和函数 使用默认参数? 好的是,您不需要为每种情况重载构造函数/方法/函数:
// Header
void doSomething(int i = 25) ;
// Source
void doSomething(int i)
{
// Do something with i
}
糟糕的是,您必须在头中声明默认值,因此您有一个隐藏的依赖性:例如,当您更改内联函数的代码时,如果您更改头中的默认值,则需要使用此头重新编译所有源,以确保它们将使用新的默认值
如果不这样做,源仍将使用旧的默认值
使用重载构造函数/方法/函数?
好的方面是,如果您的函数没有内联,那么您可以通过选择一个函数的行为方式来控制源代码中的默认值。例如:
// Use this...
class foo
{
private:
std::string name_;
unsigned int age_;
public:
foo(const std::string& name = "", const unsigned int age = 0) :
name_(name),
age_(age)
{
...
}
};
// Or this?
class foo
{
private:
std::string name_;
unsigned int age_;
public:
foo() :
name_(""),
age_(0)
{
}
foo(const std::string& name, const unsigned int age) :
name_(name),
age_(age)
{
...
}
};
// Header
void doSomething() ;
void doSomething(int i) ;
// Source
void doSomething()
{
doSomething(25) ;
}
void doSomething(int i)
{
// Do something with i
}
问题是,您必须维护多个构造函数/方法/函数,以及它们的转发。
样式的问题,但是正如Matt所说的,一定要考虑用默认参数标记构造函数,这些参数允许隐式转换为“显式”,以避免意外的自动转换。这不是一个要求(如果您正在创建一个希望隐式转换为的包装器类,则可能不可取),但它可以防止错误
我个人喜欢适当的默认值,因为我不喜欢重复的代码。YMMV.任何一种方法都有效。但是,如果有一长串可选参数,请创建一个默认构造函数,然后让set函数返回对此的引用。然后把设定器锁起来
class Thingy2
{
public:
enum Color{red,gree,blue};
Thingy2();
Thingy2 & color(Color);
Color color()const;
Thingy2 & length(double);
double length()const;
Thingy2 & width(double);
double width()const;
Thingy2 & height(double);
double height()const;
Thingy2 & rotationX(double);
double rotationX()const;
Thingy2 & rotatationY(double);
double rotatationY()const;
Thingy2 & rotationZ(double);
double rotationZ()const;
}
main()
{
// gets default rotations
Thingy2 * foo=new Thingy2().color(ret)
.length(1).width(4).height(9)
// gets default color and sizes
Thingy2 * bar=new Thingy2()
.rotationX(0.0).rotationY(PI),rotationZ(0.5*PI);
// everything specified.
Thingy2 * thing=new Thingy2().color(ret)
.length(1).width(4).height(9)
.rotationX(0.0).rotationY(PI),rotationZ(0.5*PI);
}
现在,在构建对象时,您可以选择一个选项,选择要覆盖的属性以及已设置的属性的显式命名。更具可读性:)
此外,您不再需要记住构造函数参数的顺序。答案给出了一个理由,默认参数更适合构造函数,而不是重载。我只想补充一点,C++-0x将允许从一个构造函数到另一个构造函数,从而消除了对默认值的需要