我正在学习使用对象和继承,并且我在为派生类编写构造函数时遇到了一些麻烦。
基类是一个简单的项目'标题,作者,项目编写年份和一些注释。我希望在构建项目时标题和作者字段是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
之后填充所有成员,但这似乎不太好。
提前致谢。
答案 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.
另一个建议:永远不要使用a
,t
等名称。避免更简单的短线。键入几个字符并不值得节省。无论如何,对于现代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