拥有使用默认参数的类构造函数是一种好习惯,还是应该使用单独的重载构造函数?例如:
// 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) { ... } };
这两个版本似乎都有用,例如:
foo f1; foo f2("Name", 30);
您更喜欢或推荐哪种风格?为什么?
绝对是风格问题.我更喜欢带有默认参数的构造函数,只要参数有意义.标准中的类也使用它们,这对他们有利.
需要注意的一件事是,如果除了一个参数之外的所有参数都有默认值,那么可以从该参数类型隐式转换您的类.查看此主题以获取更多信息.
我会使用默认参数,特别是因为C++不允许链接构造函数(因此您最终必须复制初始化列表,并且可能更多,为每个重载).
也就是说,有一些带有默认参数的陷阱,包括可以内联常量的事实(从而成为类的二进制接口的一部分).另一个需要注意的是,添加默认参数可以将显式多参数构造函数转换为隐式单参数构造函数:
class Vehicle { public: Vehicle(int wheels, std::string name = "Mini"); }; Vehicle x = 5; // this compiles just fine... did you really want it to?
此讨论既适用于构造函数,也适用于方法和函数.
好处是你不需要为每种情况重载构造函数/方法/函数:
// Header void doSomething(int i = 25) ; // Source void doSomething(int i) { // Do something with i }
不好的是你必须在标题中声明你的默认值,所以你有一个隐藏的依赖:就像你更改内联函数的代码一样,如果更改标题中的默认值,则需要重新编译所有源代码使用此标头确保它们将使用新的默认值.
如果不这样做,源仍将使用旧的默认值.
好处是,如果您的函数没有内联,则可以通过选择一个函数的行为来控制源中的默认值.例如:
// Header void doSomething() ; void doSomething(int i) ; // Source void doSomething() { doSomething(25) ; } void doSomething(int i) { // Do something with i }
问题是您必须维护多个构造函数/方法/函数及其转发.
根据我的经验,默认参数在当时看起来很酷,让我的懒惰因素感到高兴,但随后我正在使用课程,当默认情况开始时我很惊讶.所以我真的不认为这是个好主意; 最好有一个className :: className(),然后是className :: init(arglist).只是为了可维护性的优势.
Sam的回答给出了默认参数更适合构造函数而不是重载的原因.我只想补充一点,C++ - 0x将允许从一个构造函数到另一个构造函数的委托,从而消除了对默认值的需要.
两种方法都有效。但是,如果您有一长串可选参数,请使用默认构造函数,然后让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); }
现在,在构造对象时,您可以选择一个显式命名要覆盖的属性和已设置的属性。更具可读性:)
同样,您不再需要记住构造函数的参数顺序。