《Primer C++》第五版习题:第七章

为什么中间几章没有了呢,因为那些和C都差不多,大一学过C语言基本都会了

这本书老是惦记它的Sales_data,经常搞得我晕头转向。

7.1

这一题就是把那时的程序的Sales_item类改成Sales_data类重写一下 

#include <iostream>
#include <string>
using namespace std;

struct Sales_data
{
    string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

int main()
{
    Sales_data total;
    if (cin >> total.bookNo >> total.units_sold >> total.revenue)
    {
        Sales_data trans;
        while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue) 
        {
            if (total.bookNo == trans.bookNo) 
            {
                total.units_sold += trans.units_sold;
                total.revenue += trans.revenue;
            }
            else
            {
                cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
                total = trans;
            }
        }
        cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
    }
    else
    {
        std::cerr << "No data?!" << std::endl;
        return -1;
    }
    return 0;
}

7.2

#include <string>

struct Sales_data {
    std::string isbn() const { return bookNo; };
    Sales_data& combine(const Sales_data&);
    
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

Sales_data& Sales_data::combine(const Sales_data& rhs)
{
    units_sold += rhs.units_sold;
    revenue += rhs.revenue;
    return *this;
}

7.3

修改程序基本也是照抄。

#include<iostream>
#include<string>
using namespace std;
struct Sales_data {
    std::string isbn() const { return bookNo; };
    Sales_data& combine(const Sales_data&);

    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
//Sales_data的非接口成员函数
Sales_data add(const Sales_data&, const Sales_data&);
ostream& print(ostream&, const Sales_data&);
istream& read(istream&, Sales_data&);

Sales_data& Sales_data::combine(const Sales_data& rhs)
{
    units_sold += rhs.units_sold;
    revenue += rhs.revenue;
    return *this;
}

int main()
{
    Sales_data total;
    if (read(cin,total))
    {
        Sales_data trans;
        while (cin >> trans.bookNo >> trans.units_sold >> trans.revenue) {
            if (total.isbn() == trans.isbn())
                total.combine(trans);
            else {
                print(cout, total);
                total = trans;
            }
        }
        print(cout, total);
    }
    else
    {
        std::cerr << "No data?!" << std::endl;
        return -1;
    }

    return 0;

}

7.4

这本书的练习里让你编写的类或者函数记得留着,它经常会让你复用,到时候找不到重新写的话老麻烦了。

struct Person {
    string name;
    string address;
};

7.5

当然应该是了,因为获取函数是不会改变类里面的内容的。

struct Person {
    string name;
    string address;
    string get_name() const { return name; }
    string get_address() const { return address; }
};

7.6

#include<iostream>
#include<string>
using namespace std;
struct Sales_data {
    std::string isbn() const { return bookNo; };
    Sales_data& combine(const Sales_data&);
    int avg_price() const { return revenue / units_sold; }
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
//Sales_data的非接口成员函数
Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
    Sales_data sum = lhs;
    sum.combine(rhs);
    return sum;

}
ostream& print(ostream& os, const Sales_data& item) {
    os << item.isbn() << " " << item.units_sold << " "
        << item.revenue << " " << item.avg_price();
    return os;
}
istream& read(istream& is, Sales_data& item) {
    double price = 0;
    //输入ISBN、售出总数和售出价格
    is >> item.bookNo >> item.units_sold >> price;
    item.revenue = price * item.units_sold;
    return is;
}

Sales_data& Sales_data::combine(const Sales_data& rhs)
{
    units_sold += rhs.units_sold;
    revenue += rhs.revenue;
    return *this;
}

7.7

#include<iostream>
#include<string>
using namespace std;
struct Sales_data {
    std::string isbn() const { return bookNo; };
    Sales_data& combine(const Sales_data&);
    int avg_price() const { return revenue / units_sold; }
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
//Sales_data的非接口成员函数
Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
    Sales_data sum = lhs;
    sum.combine(rhs);
    return sum;

}
ostream& print(ostream& os, const Sales_data& item) {
    os << item.isbn() << " " << item.units_sold << " "
        << item.revenue << " " << item.avg_price();
    return os;
}
istream& read(istream& is, Sales_data& item) {
    double price = 0;
    //输入ISBN、售出总数和售出价格
    is >> item.bookNo >> item.units_sold >> price;
    item.revenue = price * item.units_sold;
    return is;
}

Sales_data& Sales_data::combine(const Sales_data& rhs)
{
    units_sold += rhs.units_sold;
    revenue += rhs.revenue;
    return *this;
}


int main()
{
    Sales_data total;
    if (read(cin, total))
    {
        Sales_data trans;
        while (read(cin,trans)) {
            if (total.isbn() == trans.isbn())
                total = add(total, trans);
            else {
                print(cout, total);
                total = trans;
            }
        }
        print(cout, total);
    }
    else
    {
        std::cerr << "No data?!" << std::endl;
        return -1;
    }

    return 0;

}

7.8

因为read需要修改Sales_data,所以是普通的引用,print只是打印数据,并不修改它,所以是常量引用。

7.9

#include<iostream>
#include<string>
using namespace std;
struct Person {
    string name;
    string address;
    string get_name() const { return name; }
    string get_address() const { return address; }
};
istream& read(istream& is, Person& p) {
    is >> p.name >> p.address;
    return is;
}
ostream& print(ostream& os, const Person& p) {
    os << p.name << " " << p.address;
    return os;
}

7.10

连续读入data1和data2

7.11

#include<iostream>
#include<string>
using namespace std;
struct Sales_data {
    //构造函数是新加的
    Sales_data() = default;
    Sales_data(const string &s):bookNo(s){}
    Sales_data(const string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){}
    Sales_data(istream&);
    std::string isbn() const { return bookNo; };
    Sales_data& combine(const Sales_data&);
    int avg_price() const { return revenue / units_sold; }
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
Sales_data::Sales_data(istream& is) {
    read(is, *this);//这个read前面有,就不再贴定义了
}

7.12

struct Sales_data {
    //构造函数是新加的
    Sales_data() = default;
    Sales_data(const string &s):bookNo(s){}
    Sales_data(const string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){}
    Sales_data(istream& is) {
        read(is, *this);
    }
    std::string isbn() const { return bookNo; };
    Sales_data& combine(const Sales_data&);
    int avg_price() const { return revenue / units_sold; }
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

7.13

#include<iostream>
#include<string>
using namespace std;
struct Sales_data;
istream& read(istream& is, Sales_data& item);
struct Sales_data {
    //构造函数是新加的
    Sales_data() = default;
    Sales_data(const string &s):bookNo(s){}
    Sales_data(const string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){}
    Sales_data(istream& is) {
        read(is, *this);
    }
    std::string isbn() const { return bookNo; };
    Sales_data& combine(const Sales_data&);
    int avg_price() const { return revenue / units_sold; }
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

//Sales_data的非接口成员函数
Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
    Sales_data sum = lhs;
    sum.combine(rhs);
    return sum;

}
ostream& print(ostream& os, const Sales_data& item) {
    os << item.isbn() << " " << item.units_sold << " "
        << item.revenue << " " << item.avg_price();
    return os;
}
istream& read(istream& is, Sales_data& item) {
    double price = 0;
    //输入ISBN、售出总数和售出价格
    is >> item.bookNo >> item.units_sold >> price;
    item.revenue = price * item.units_sold;
    return is;
}

Sales_data& Sales_data::combine(const Sales_data& rhs)
{
    units_sold += rhs.units_sold;
    revenue += rhs.revenue;
    return *this;
}

int main()
{
    Sales_data total(cin);
    if (!total.bookNo.empty())
    {
        while (cin) {
            Sales_data trans(cin);
            if (!cin) break;
            if (total.isbn() == trans.isbn())
                total = add(total, trans);
            else {
                print(cout, total);
                total = trans;
            }
        }
        print(cout, total);
    }
    else
    {
        std::cerr << "No data?!" << std::endl;
        return -1;
    }

    return 0;

}

7.14

struct Sales_data;
istream& read(istream& is, Sales_data& item);
struct Sales_data {
    Sales_data() = default;
    //修改了一下这个构造函数
    Sales_data(const string &s):bookNo(s),units_sold(0),revenue(0.0){}
    Sales_data(const string &s,unsigned n,double p):bookNo(s),units_sold(n),revenue(p*n){}
    Sales_data(istream& is) {
        read(is, *this);
    }
    std::string isbn() const { return bookNo; };
    Sales_data& combine(const Sales_data&);
    int avg_price() const { return revenue / units_sold; }
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};

7.15

struct Person {
    string name;
    string address;
    Person() = default;
    Person(const string& n, const string& a) :name(n), address(a) {}
    string get_name() const { return name; }
    string get_address() const { return address; }
};

7.16

次数没有限定,位置应该也没有。

对外接口应该定义在public之后。内部不可见的数据应该定义在private之后。

7.17

有一点。默认访问权限不同。struct的默认访问权限是public,class的默认访问权限是private

7.18

封装的主要目的是隐藏类的实现细节,只暴露对外部代码有意义的接口。

7.19

class Person {
public:
    Person() = default;
    Person(const string& n, const string& a) :name(n), address(a) {}
    string get_name() const { return name; }
    string get_address() const { return address; }
private:
    string name;
    string address;
};

姓名和地址是一个人的私有数据,不对外开放,用户不能修改,只能通过对外接口get来得到。

7.20

友元的使用场景:

  1. 访问私有成员: 当一个类需要将其他类或函数作为友元时,这些友元可以访问被声明为私有的该类成员。

  2. 提高性能: 在一些情况下,为了提高性能,可能需要允许其他类或函数直接访问私有成员,而不通过公共接口。

友元的利与弊:

利:

  1. 访问私有成员: 友元能够访问被声明为私有的类成员,这在某些特殊情况下是有用的,例如提供更高效的实现或解决特定的设计问题。

  2. 灵活性: 友元提供了一定的灵活性,允许某些类或函数绕过访问控制规则,更灵活地设计和实现一些特殊的功能。

弊:

  1. 破坏封装性: 友元可能破坏了封装性,使得某些类的实现细节对外部代码可见,增加了代码的耦合性。

  2. 难以维护: 使用友元会增加代码的复杂性,降低代码的可读性和可维护性。当类的实现发生变化时,可能会影响到多个类和函数。

  3. 安全性问题: 友元提供了一定程度的权限,如果不慎使用,可能导致安全性问题,因为友元能够绕过正常的访问控制。

 7.21

#include<iostream>
#include<string>
#include<queue>
using namespace std;
class Sales_data {
    friend ostream& print(ostream& os, const Sales_data& item);
    friend istream& read(istream& is, Sales_data& item);
public:
    Sales_data() = default;
    Sales_data(const string& s) :bookNo(s) {}
    Sales_data(const string& s, unsigned n, double p) :bookNo(s), units_sold(n), revenue(p* n) {}
    Sales_data(istream&);
    std::string isbn() const { return bookNo; };
    Sales_data& combine(const Sales_data&);
private:
    int avg_price() const { return revenue / units_sold; }
    std::string bookNo;
    unsigned units_sold = 0;
    double revenue = 0.0;
};
istream& read(istream& is, Sales_data& item);
Sales_data::Sales_data(istream& is) {
    read(is, *this);//这个read前面有,就不再贴定义了
}
//Sales_data的非接口成员函数
Sales_data add(const Sales_data& lhs, const Sales_data& rhs) {
    Sales_data sum = lhs;
    sum.combine(rhs);
    return sum;

}
ostream& print(ostream& os, const Sales_data& item) {
    os << item.isbn() << " " << item.units_sold << " "
        << item.revenue << " " << item.avg_price();
    return os;
}
istream& read(istream& is, Sales_data& item) {
    double price = 0;
    //输入ISBN、售出总数和售出价格
    is >> item.bookNo >> item.units_sold >> price;
    item.revenue = price * item.units_sold;
    return is;
}

Sales_data& Sales_data::combine(const Sales_data& rhs)
{
    units_sold += rhs.units_sold;
    revenue += rhs.revenue;
    return *this;
}

int main()
{
    Sales_data total;
    if (read(cin, total))
    {
        Sales_data trans;
        while (read(cin, trans)) {
            if (total.isbn() == trans.isbn())
                total = add(total, trans);
            else {
                print(cout, total);
                total = trans;
            }
        }
        print(cout, total);
    }
    else
    {
        std::cerr << "No data?!" << std::endl;
        return -1;
    }

    return 0;
}

7.22

class Person {
public:
    Person() = default;
    Person(const string& n, const string& a) :name(n), address(a) {}
    string get_name() const { return name; }
    string get_address() const { return address; }
private:
    string name;
    string address;
};

7.23

#include <string>

class Screen {
    public:
        using pos = std::string::size_type;
        char get() const { return contents[cursor]; }
        char get(pos r, pos c) const { return contents[r*width+c]; }

    private:
        pos cursor = 0;
        pos height = 0, width = 0;
        std::string contents;
};

7.24

class Screen {
public:
    using pos = std::string::size_type;

    Screen() = default;
    Screen(pos ht, pos wd, char c) :height(ht), width(wd), contents(ht* wd, c) { }
    Screen(pos ht, pos wd) :height(ht), width(wd), contents(ht* wd, ' ') { }
    char get() const { return contents[cursor]; }
    char get(pos r, pos c) const { return contents[r * width + c]; }

private:
    pos cursor = 0;
    pos height = 0, width = 0;
    std::string contents;
};

7.25

可以,因为它的数据都是可以默认拷贝的类型,也没有指针

7.26

加个inline就行了,这就不写了

7.27 

    //这些都是写在类内部的
    Screen &move(pos r,pos c){
        pos  row = r * width;
        cursor = row + c;
        return *this;
    }
    Screen& set(char c) {
        contents[cursor] = c;
        return *this;
    }
    Screen& set(pos r,pos col,char c) {
        contents[r*width+col] = c;
        return *this;
    }
    Screen& display(ostream& os) { do_display(os); return *this; }
    const Screen &display(ostream &os) const{ do_display(os); return *this; }

7.28

两次display的显示内容不同。如果不加引用,第一次对myScreen的操作其实是对其一个副本的操作,myScreen的内容并没有改变,所以两次显示的内容不同且第二次display的还是原来myScreen的内容。

7.29

自行修改验证

7.30

优点:

  1. 明确性:使用 this 指针可以明确地指出你正在访问当前对象的成员,这可以帮助避免混淆或歧义。
  2. 区分局部变量和成员变量:当成员变量的名称与局部变量相同时,使用 this 指针可以明确地指出你要访问的是成员变量而不是局部变量。
  3. 在某些情况下,可以避免命名冲突:如果在类的成员函数中有多个变量具有相同的名称(例如,在不同的成员函数中参数名称相同),使用 this 指针可以帮助避免名称冲突。

缺点:

  1. 繁琐:在每次访问成员变量时都使用 this 指针可能会使代码显得冗长,尤其是在简单的情况下,这样做可能会让代码难以阅读和理解。
  2. 不必要的:在许多情况下,可以省略 this 指针而直接访问成员变量,特别是当没有名称冲突时。
  3. 可读性降低:过度使用 this 指针可能会降低代码的可读性,因为它使代码更加冗长,而且可能会使代码难以理解。

 7.31

class Y;
class X {
    Y* ptr;
};
class Y {
    X x;
};

7.32

class Screen;
class Window_mgr {
public:
    using ScreenIndex = std::vector<Screen>::size_type;
    void clear(ScreenIndex);
private:
    vector<Screen> screens;
};
class Screen {
    friend void Window_mgr::clear(ScreenIndex);
public:
    using pos = std::string::size_type;

    Screen() = default;
    Screen(pos ht, pos wd, char c) :height(ht), width(wd), contents(ht* wd, c) { }
    Screen(pos ht, pos wd) :height(ht), width(wd), contents(ht* wd, ' ') { }
    char get() const { return contents[cursor]; }
    char get(pos r, pos c) const { return contents[r * width + c]; }
    Screen &move(pos r,pos c){
        pos  row = r * width;
        cursor = row + c;
        return *this;
    }
    Screen& set(char c) {
        contents[cursor] = c;
        return *this;
    }
    Screen& set(pos r,pos col,char c) {
        contents[r*width+col] = c;
        return *this;
    }
    Screen& display(ostream& os) { do_display(os); return *this; }
    const Screen &display(ostream &os) const{ do_display(os); return *this; }
private:
    void do_display(ostream& os)const { os << contents; }
    pos cursor = 0;
    pos height = 0, width = 0;
    std::string contents;
};
void Window_mgr::clear(ScreenIndex i) {
    Screen& s = screens[i];
    s.contents = string(s.height * s.width, ' ');
}

7.33

Screen::pos Screen::size() const {
    return height * width;
}

 7.34

会报错

7.35

在外面定义的setVal的Type是string,initVal则是类内部的函数

这么写会报错,需要把Type改成Exercise::Type

7.36

rem的初值改成i%j

7.37

next用了默认构造函数

last使用了Sales_data(const string& s) :bookNo(s) {}这个构造函数

7.38

Sales_data(istream& i=std::cin);

7.39

不合法,这样就不知道调用哪个默认构造函数了

7.40略

 7.43

class NoDefault {
public:
    NoDefault(int d):data(d){}
    int data;
};
class C {
public:
    C():member(1){}
    NoDefault member;
};

7.44

不合法,因为NoDefault没有默认构造函数

7.45

合法,C有默认构造函数

7.46(答案不确定)

(a)不对,构造函数可以被删除

(b)不对,可以是有缺省参数的构造函数

(c)对

(d)不对,如果定义了其他构造函数则编译器不会为类生产默认的构造函数

7.47

这个看自己需求吧,如果使用explicit的话就不允许通过传递string类型来隐式构造Sales_data 

7.48

是不是explicit都没有影响

7.49

a可运行

b不可运行,因为生成的Sales_data是一个临时对象,不能传递给引用

c也不可运行,因为i不是const类型的Sales_data

7.50略

7.51

如果vector的单参数不是explicit,那一个int数字都可以隐式转化成vector,这是很不合理的。

7.53

class Debug {
public:
    constexpr Debug(bool b=true):hw(b),io(b),other(b){}
    constexpr Debug(bool h,bool i,bool o):hw(h),io(i),other(o){}
    constexpr bool any() { return hw || io || other; }
    void set_io(bool b) { io = b; }
    void set_hw(bool b) { hw = b; }
    void set_other(bool b) { other = b; }
private:
    bool hw;
    bool io;
    bool other;
};

7.54

不可以,常量函数的唯一可执行语句就是返回语句,set_需要设置

7.55

不是,它的成员不都是字面值类型。

7.56

类的静态成员是属于类本身而不是类的实例的成员变量或成员函数。可以通过类名来访问静态成员,而不需要创建类的实例。静态成员在整个类的所有实例之间共享,它们的生命周期与程序的生命周期相同。

静态成员的优点包括:

  1. 共享数据:静态成员被类的所有实例共享,因此它们可以用来表示属于类的数据或状态,而不是特定于类的实例。

  2. 全局访问:可以直接通过类名来访问静态成员,无需创建类的实例。这使得静态成员成为了一个类的全局变量,可以在整个程序中方便地访问。

  3. 与类紧密关联:静态成员与类的实例无关,它们与类本身密切相关。这意味着它们可以用于表示类级别的属性或行为,而不是实例级别的。

静态成员与普通成员的区别包括:

  1. 内存分配:静态成员在程序启动时分配内存,直到程序结束才会释放,而普通成员在每个类的实例创建时分配内存,并在实例销毁时释放。

  2. 访问方式:静态成员可以直接通过类名来访问,而普通成员需要通过类的实例来访问。

  3. 生命周期:静态成员的生命周期与程序的生命周期相同,而普通成员的生命周期与所属的类的实例相关。

  4. 作用范围:静态成员是类级别的,所有的类实例共享同一个静态成员。而普通成员是实例级别的,每个类实例都有自己的一份普通成员数据。

7.57

class Account {
public:
    void calculate() { amount += amount * interestRate; }
    static double rate() { return interestRate; }
    static void rate(double newRate) { interestRate = newRate; }
    
private:
    std::string owner;
    double amount;
    static double interestRate;
    static constexpr double todayRate = 42.42;
    static double initRate() { return todayRate; }
};

double Account::interestRate = initRate();

7.58

rate不可以,因为rate不是常量类型

vec不可以,因为vector也不是常量

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值