构造函数重载和默认参数

时间:2016-03-13 19:27:28

标签: c++ c++11

我正在学习使用对象和继承,并且我在为派生类编写构造函数时遇到了一些麻烦。

基类是一个简单的项目'标题,作者,项目编写年份和一些注释。我希望在构建项目时标题和作者字段是mandatore,但另一个是可选的。所以我编写了一个带有两个可选参数的构造函数。

// item.hpp

class Item
{
public:
    std::string title;
    std::string author;

    unsigned int year;

    std::string notes;

    Item(const std::string &t,
         const std::string &a,
         const unsigned int y = 0,
         const std::string &n = "")
    : title{t}, author{a}, year{y}, notes{n}
    { };
}

现在我有了一个派生类:' Book',我正在尝试编写构造函数但是不断收到错误,而这些错误会导致对Book的构造函数的调用不明确'

// book.hpp

class Book : public Item
{
public:
    unsigned int pages;
    std::string series;

    // Fill only the fields for Item
    Book(const std::string &t,
         const std::string &a,
         const unsigned int y = 0,
         const std::string &n = "")
    : Item(t, a, y, n)
    { };

    // Fill all the fields for Book
    Book(const std::string &t,
         const std::string &a,
         const unsigned int y = 0,
         const std::string &n = "",
         const unsigned int p = 0,
         const std::string &s = "")
    : Item(t, a, y, n), pages{p}, series{s}
    { };

    // Maybe there's no notes
    Book(const std::string &t,
         const std::string &a,
         const unsigned int y = 0,
         const unsigned int p = 0,
         const std::string &s = "")
    : Item(t, a, y), pages{p}, series{s}
    { };
}

现在,如果我试图建立一本这样的书:

Book b("a", "b");

我得到构造函数调用不明确的错误。

我的问题是:我应该如何解决想要使用默认参数的多个构造函数的问题,以便我不必填写所有参数'?

我认为第一个构建者是' Book'可以删除,因为它是一个子集'第二个。但我不知道如何处理第三个问题。有什么建议吗?

编辑:我还考虑过构建空对象,然后在b.member = whatever之后填充所有成员,但这似乎不太好。

提前致谢。

4 个答案:

答案 0 :(得分:4)

Book类的所有构造函数都具有相同数量和类型的非默认参数。这意味着当您只提供这些时,编译器无法分辨它们中的哪一个使用,并且会给您一个模糊的调用错误。你真的只需要一个其余的默认值,因为当你实际有你要填写的东西时,你只会调用其他的,所以尝试从除了一个构造函数之外的所有构造函数中删除默认值。

答案 1 :(得分:2)

这是因为您有2个或更多Book构造函数可以使用Book("a", "b")调用。此问题与默认参数无直接关系。即使其中一个构造函数具有所有参数的默认值,也会发生此错误。应该只有一个可以使用两个字符串调用的构造函数。

为了证明此问题与非默认参数的数量无关,这会导致相同的错误,因为这些构造函数也可以使用两个字符串调用:

// Can be called with two string args:
Book(const std::string &t = "",
     const std::string &a = "")
: Item(t, a, 0, "")
{ };

// This can also be called with two string args:
Book(const std::string &t,
     const std::string &a = "")
: Item(t, a, 0, "")
{ };

从设计的角度来看,你的3个构造函数似乎是多余的,它们或多或少具有不同顺序的相同参数。在您的情况下,解决方案将只使用一个具有最多参数的构造函数,并将所有可能的参数组合在一起。排序默认参数,以便最常用的参数位于参数列表中较低的索引处。

我个人讨厌具有大量参数的函数和构造函数。如果参数数量达到约5或更多,我通常建议使用以下模式:

创建一个结构,其中包含函数调用的所有参数作为成员变量。将const ref作为构造函数或函数的唯一参数传递给struct:

struct SBookInfo
{
    std::string t;
    std::string a;
    unsigned int y;
    std::string n;

    SBookInfo()
    {
        y = 0;
    }

    // TODO: provide static NAMED factory
    // methods that don't have thousands of arguments
    static SBookInfo CreateWhatever(unsigned int _y=0)
    {
        SBookInfo info;
        info.a = "whatever";
        info.t = "woof";
        info.y = _y;
        return info;
    }
}

// Book constructor
Book(const SBookInfo& info);

这样,使用您的代码的人不必担心参数顺序和类似的东西,构建Book实例的代码在任何地方变得更加清晰和可读:

// With this pattern you can give default value to any args.
// The user of the constructor can specify the args in any order.
SBookInfo info;
info.y = 6;
info.a = "woof";
info.t = "woof";
Book book(info);

// For some special cases the info struct can have NAMED
// factory methods that makes the code more readable.
Book book2(SBookInfo.CreateWhatever(5));

// The above code is much more obvious to read than
// the original. By reading this code who could tell
// me the name of the 3rd argument where we pass 6 to the ctor?
// Book book("woof", "woof", 6);
// And it would become even cleaner with growing number of args.

另一个建议:永远不要使用at等名称。避免更简单的短线。键入几个字符并不值得节省。无论如何,对于现代IDE,您必须在第一次命名标识符时键入名称,稍后自动完成将帮助您避免键入。

答案 2 :(得分:1)

W /委托构造函数:

Book(const std::string &t,
     const std::string &a)
: Book(t, a, 0, "")
{ }

Book(const std::string &t,
     const std::string &a,
     const unsigned int y,
     const unsigned int p = 0,
     const std::string &s = "")
: Book(t, a, y, "", p, s)
{ }

Book(const std::string &t,
     const std::string &a,
     const unsigned int y,
     const std::string &n,
     const unsigned int p = 0,
     const std::string &s = "")
: Item(t, a, y, n), pages{p}, series{s}
{ }

没有委托构造函数:

Book(const std::string &t,
     const std::string &a)
: Item(t, a), pages{0}, series{""}
{ }

Book(const std::string &t,
     const std::string &a,
     const unsigned int y,
     const unsigned int p = 0,
     const std::string &s = "")
: Item(t, a, y), pages{p}, series{s}
{ }

Book(const std::string &t,
     const std::string &a,
     const unsigned int y,
     const std::string &n,
     const unsigned int p = 0,
     const std::string &s = "")
: Item(t, a, y, n), pages{p}, series{s}
{ }

答案 3 :(得分:0)

class Item {
protected:
    std::string mTitle;
    std::string mAuthor;
    std::string mNotes;
    unsigned int mYear;

public:
    Item( const std::string& title, const std::string& author, unsigned int year = 0, const std::string& notes = std::string() );        

};

Item::Item( const std::string& title, const std::string& author, unsigned int year, const std::string& notes ) :
mTitle( title ),
mAuthor( author ),
mYear( year ),
mNotes( notes ) {
} // Item

class Book : public Item {
private:
    std::string mSeries;
    unsigned int mPages;

public:
    Book( const std::string& title, const std::string& author, unsigned int year = 0, unsigned int pages = 0,  const std::string& series = 0, const std::string& notes = std::string() );       

};

Book::Book( const std::string& title, const std::string& author, unsigned int year, unsigned int pages, const std::string& series, std::string& notes ) :
Item( title, author, year, notes ),
mSeries( series ),
mPages( pages ) {
} // Book

现在我确实使Item的成员受到保护,以便任何派生类(如Book)可以直接访问它们,而Book I中的成员都是私有的,因此您需要访问器函数或方法来修改或检索这些成员。外部对象或代码源。

修改 在Book的构造函数中,我稍微改变了参数的顺序。我向左移动了页面数,并将注释移动到参数调用右侧的默认参数。我之所以这样做是因为在有可用笔记之前,它更有可能使用可用页数调用此构造函数,并且我还移动了系列并将笔记作为最后一个参数。

int main() {
    // Different Ways To Call this using default parameters
    Book b1( "Fellowship of the Ring", "Tolkien", 1958, 387, "Lord of the Rings" ); // No Notes

    Book b2( "Apostle of John" "John", 27, 80, std::string() or " ", "A book of the New Testament found in the Holy Bible" ); // Notes, but not series.

    return 0;
} // main