《C++ Primer》第7章 7.5节习题答案

《C++ Primer》第7章 类

7.5节 构造函数再探

练习7.36:下面的初始值是错误的,请找出问题所在并尝试修改化。
struct X {
        X (int i, int j): (base(i), rem(base % j) {  }
        int rem, base;
};

【出题思路】

本题旨在考查使用构造函数初始值列表时成员的初始化顺序,初始化顺序只与数据成员在类中出现的次序有关,而与初始值列表的顺序无关。
答:
在类X中,两个数据成员出现的顺序是rem在前,base在后,所以当执行X对象的初始化操作时先初始化rem,如上述代码所示,初始化rem要用到base的值,而此时base尚未初始化,因此会出现错误。该过程与构造函数初始值列表中谁现在在前面谁出现在后面没有任何关系。
修改的方法是,只需把变量rem和base的次序调换即可,形式如下:
struct X {
        X (int i, int j): (base(i), rem(base % j) {  }
        int base, rem;  //成员的初始化顺序与它们在类定义中的出现顺序一致:base比rem先初始化,这与构造函数初始值列表没有关系
};
练习7.37:使用本节提供的类,确定初始化下面的变量时分别使用了哪个构造函数,然后罗列出每个对象所有数据成员的值。
Sales_data first_item(cin);
int main() {
        Sales_data next;
        Sales_data last("9-999-99999-9")
}

【出题思路】

根据实参的不同调用实现了最佳匹配的构造函数,对于没有提供实参的成员使用其类内初始值进行初始化。
答: Sales_data first_item(cin); 使用了接受std::istream&参数的构造函数,该对象的成员值依赖于用户的输入。
       Sales_data next;使用了Sales_data的默认构造函数,其中string类型的成员bookNo默认初始化为空字符串,其他几个成员使用类内初始值初始化为0;
        Sales_data last("9-999-99999-9");使用了接受const string&参数的构造函数,其中bookNo使用实参初始化为"9-999-99999-9",其他几个成员使用类内初始值初始化为0;
练习7.38:有些情况下我们希望提供cin作为接受istream&参数的构造函数的默认实参,请声明这样的构造函数。
【出题思路】
可以直接在函数声明的地方为istream&类型的参数设置默认实参cin
答:满足题意的构造函数如下所示:        
Sales_data(std::istream &is = std::cin)
{
      is >> *this;
}

此时该函数具有了默认构造函数的作用,因此我们原来声明的默认构造函数Sales_data() = default;应该去掉,否则会引起调用的二义性。

练习7.39:如果接受string的构造函数和接受istream&的构造函数都使用默认实参,这种行为合法吗?如果不,为什么?
【出题思路】
本题考查使用默认实参对构造函数的影响。
答:如果我们为构造函数的全部形参都提供了默认实参(包括为只接受一个形参的构造函数提供默认实参),则该构造函灵敏同时具备了默认构造函数的作用。此时即使我们不提供任何实参地创建类的对象,也可以找到可用的构造函数。
        然而,如果按照本题的叙述,我们为两个构造函数同样都赋予了默认实参,则这两个构造函数都具有了默认构造函数的作用。一旦我们不提供任何参数地创建类的对象,则编译器无法判断这两个(重载的)构造函数哪个更好,从而出现了二义性错误。

练习7.40:从下面的抽象概念中选择一个(或者你自己指定一个),思考这样的类需要哪些数据成员,提供一组合理的构造函数并阐明这样做的原因。

(a)Book            (b)Date            (c)Employee

(d)Vehicle        (e)Object          (f)Tree

【出题思路】

掌握创建类类型的方法,理解不同构造函数的区别。

【解答】

首先选择(a)Book,一本书通常包含书名、ISBN编号、定价、作者、出版社等信息,因此令其数据成员为:Name、ISBN、Price、Author、Publisher,其中Price是double类型,其他都是string类型。Book的构造函数有三个:一个默认构造函数、一个包含完整书籍信息的构造函数和一个接受用户输入的构造函数。其定义如下:

#include <iostream>
#include <string>

using namespace std;

class Book
{
    //这里一定要重载>>操作符 否则Book(std::istream &is)会报
    //invalid operands to binary expression('std::istream'(aka 'basic_istream<char>') and 'Book')的错误
    friend std::istream& operator >> (std::istream&, Book&);
private:
    string Name, ISBN, Author, Publisher;
    double Price = 0;

public:
    Book() = default;
    Book(const string &n, const string &I, double pr, const string &a, const string &p)
    {
        Name = n;
        ISBN = I;
        Price = pr;
        Author = a;
        Publisher = p;
    }

    Book(std::istream &is)
    {
        is >> *this;
    }

};

//接收5个参数
std::istream& operator >> (std::istream& in, Book& s)
{
    in >> s.Name >> s.ISBN >> s.Author >> s.Publisher >> s.Price;
    // check that the inputs succeeded
    if (in)
    {
        cout << "Name==============" << s.Name << endl;
        cout << "ISBN==============" << s.ISBN << endl;
        cout << "Author============" << s.Author << endl;
        cout << "Publisher=========" << s.Publisher << endl;
        cout << "price=============" << s.Price << endl;
    }
    else
    {
        s = Book();  // input failed: reset object to default state
    }
    return in;
}

int main()
{
    cout << "please input data====" << endl;

    Book book(cin);
    cout << "input finished===============" << endl;
    return 0;
}

运行结果:

 

练习7.41:使用委托构造函数重新编写你的Sales_data类,给每个构造函数体添加一条语句,令期一量执行就打印一条信息。用千种可能的方式分别创建Sales_data对象,认真研究每次输出的信息直到你确实理解了委托构造函数的执行顺序。

【出题思路】

委托构造函数使用它所属类的其他构造函数执行它自己的初始化过程,或者说它把它自己的一些或全部职责委托给了其他构造函数。程序先执行受委托构造函数,然后才执行委托构造函数本身的语句。

【解答】

改写后的Sales_data类及其验证程序如下所示:

#include <iostream>
#include <string>


using namespace std;

class Sales_data
{
    friend std::istream &read(std::istream &is, Sales_data &item);
    friend std::ostream &print(std::ostream &os, const Sales_data &item);

public:
    Sales_data(const string &book, unsigned num, double sellp, double salep)
        :bookNo(book), units_sold(num), sellingprice(sellp), saleprice(salep)
    {
        if(sellingprice)
            discount = saleprice / sellingprice;
        cout << "该构造函数接受书号、销售量、原价、实际售价四个信息" << endl;
    }

    Sales_data() : Sales_data("", 0, 0, 0)
    {
        cout << "该构造函数无须接受任何信息" << endl;
    }

    Sales_data(const string &book):Sales_data(book, 0, 0, 0)
    {
        cout << "该构造函数接受书号信息" << endl;
    }

    Sales_data(std::istream &is): Sales_data()
    {
        read(is, *this);
        cout << "该构造函数接受用户输入的信息" << endl;
    }

private:
    std::string bookNo;                 //书籍编号,隐式初始化为空串
    unsigned units_sold = 0;            //销售量,显式初始化为0
    double sellingprice = 0.0;          //原始价格,显式初始化为0.0
    double saleprice = 0.0;             //实售价格,显式初始化为0.0
    double discount = 0.0;              //折扣,显式初始化为0.0
};

std::istream &read(std::istream &is, Sales_data &item)
{
    is >> item.bookNo >> item.units_sold >> item.sellingprice >> item.saleprice;
    return is;
}

std::ostream &print(std::ostream &os, const Sales_data &item)
{
    os << item.bookNo << " " << item.units_sold << " " << item.sellingprice << " " << item.saleprice << " " << item.discount << endl;
    return os;
}

int main()
{
    cout << "第一个对象=========" << endl;
    Sales_data first("987-7-121-15535-2", 85, 128, 109);
    cout << "\n第二个对象=========" << endl;
    Sales_data second;
    cout << "\n第三个对象=========" << endl;
    Sales_data third("987-7-121-15535-2");
    cout << "\n第四个对象=========" << endl;
    Sales_data last(cin);

    return 0;
}

运行结果:

 

练习7.42:对于你在练习7.40(参见7.5.1节,第261页)中编写的类,确定哪些构造函数可以使用委托。如果可以的话,编写委托构造函数。如果不可以,从抽象概念列表中重新选择一个你认为可以使用委托构造函数的,为挑选出的这个概念编写类定义。

【出题思路】

委托构造函数是指使用它所属类的其他构造函数执行它自己的初始化过程,因此在类中应该设计一些构造函数使其具备自主的构造函数功能,而把另外一些设计成委托构造函数。

【解答】

以练习7.40构建的Book类为例,我们令其中的构造函数Book(const string &n,const string &I, double pr, const string &a, const string &p)为普通构造函数,而令另外两个作为委托构造函数。其具体形式如下所示:

#include <iostream>
#include <string>

using namespace std;

class Book
{
    //这里一定要重载>>操作符 否则Book(std::istream &is)会报
    //invalid operands to binary expression('std::istream'(aka 'basic_istream<char>') and 'Book')的错误
    friend std::istream& operator >> (std::istream&, Book&);

private:
    string Name, ISBN, Author, Publisher;
    double Price = 0;

public:
    Book(const string &n, const string &I, double pr, const string &a, const string &p)
    {
        Name = n;
        ISBN = I;
        Price = pr;
        Author = a;
        Publisher = p;
        cout << "第一个构造函数=================" << endl;
    }
    Book() : Book("", "", 0, "", "")
    {
        cout << "第2个构造函数=================" << endl;
    }
    //Book(std::istream &is)
    Book(std::istream &is): Book()
    {
        is >> *this;
    }

    void print()
    {
        cout << "Name = " << Name << "\nISBN = " << ISBN << "\nAuthor = " << Author << "\nPublisher = " << Publisher << endl;
    }

};


//接收5个参数
std::istream& operator >> (std::istream& in, Book& s)
{

    in >> s.Name >> s.ISBN >> s.Author >> s.Publisher >> s.Price;
    // check that the inputs succeeded
    if (in)
    {
        cout << "Name=======org=======" << s.Name << endl;
        cout << "ISBN=======org=======" << s.ISBN << endl;
        cout << "Author=====org=======" << s.Author << endl;
        cout << "Publisher==org=======" << s.Publisher << endl;
        cout << "price======org=======" << s.Price << endl;
    }
    else
    {
        s = Book();  // input failed: reset object to default state
    }
    return in;
}

int main()
{
    cout << "please input data====" << endl;
    Book book(cin);
    cout << "input finished===============" << endl;
    book.print();
    return 0;
}

运行结果:

 

练习7.43:假定有一个名为NoDefault的类,它有一个接受int的构造函数,但是没有默认构造函数。定义类C,C有一个NoDefault类型的成员,定义C的默认构造函数。

【出题思路】

因为NoDefault仅有的一个构造函数并不是默认构造函数,所以在类C中,不能使用无参数的默认构造函数,那样的话,类C的NoDefault成员无法正确初始化。

【解答】

我们需要为类C的构造函数提供一个默认的int值作为参数,满足题意的类定义及验证程序如下所示:

#include <iostream>
#include <string>

using namespace std;

//该类型没有显式定义默认构造函数,编译器也不会为它合成一个
class NoDefault
{
public:
    NoDefault(int i)
    {
        val = i;
        cout << "NoDefault构造函数===========" << endl;
    }
    int val;
};

class C
{
public:
    NoDefault nd;
    //必须显式调用Nodefault的带参构造函数初始化nd
    C(int i = 0): nd(i)
    {
        cout << "C构造函数===========" << endl;
    }
};

int main()
{
    C c;  //使用了类型C的默认构造函数
    cout << c.nd.val << endl;
    return 0;
}

运行结果:

 

练习7.44:下面这条声明合法吗?如果不,为什么?
        vector< NoDefault> vec(10);
【出题思路】
理解默认构造函数的用法,理解vector对象是如何定义和初始化的。
答:上述语句的含义是创建一个vector对象vec,该对象包含10个元素,每个元素的类型都是NoDefault且执行默认初始化。然而,因为我们在类NoDefault的定义中没有设计默认构造函数,所以所需要的默认初始化过程无法执行。编译器会报告这一错误。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值