• C++ Primer第5版 第七章课后练习答案


    练习7.1

    struct Sales_data {
        string bookNo;
        unsigned units_sold = { 0 };
        double revenue = { 0.0 };
    };
    
    int main(int argc, char* argv[])
    {   
        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.revenue += trans.revenue;
                    total.units_sold += trans.units_sold;
                }
                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?!" << endl;
            return -1;
        }
        return 0;
    }

    练习7.2

    struct Sales_data {
        string bookNo;
        unsigned units_sold = { 0 };
        double revenue = { 0.0 };
    
        string isbn() { return bookNo; }
        Sales_data& combine(const Sales_data& rhs) {
            units_sold += rhs.units_sold;
            revenue += rhs.revenue;
            return *this;
        }
    };

    练习7.3

    int main(int argc, char* argv[])
    {   
        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.isbn() == trans.isbn()) {
                    total.combine(trans);
                }
                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?!" << endl;
            return -1;
        }
        return 0;
    }

    练习7.4

    #ifndef PERSON_H_
    #define PERSON_H_
    #include <string>
    using std::string;
    struct Person {
        string name;
        string address;
    };
    #endif // !PERSON_H_

    练习7.5

    struct Person {
        string name;
        string address;
        const string isname() { return name; }
        const string isaddress() { return address; }
    };

    应该是const的,因为我们不需要修改返回值

    练习7.6

    Sales_data add(Sales_data& lhs, Sales_data& rhs) {
        Sales_data sum = lhs;
        sum.combine(rhs);
        return sum;
    }
    
    istream &read(istream& is, Sales_data& it) {
        is >> it.bookNo >> it.units_sold >> it.revenue;
        return is;
    }
    
    ostream& print(ostream& os, Sales_data& it) {
        os << it.isbn() << " " << it.units_sold << " " << it.revenue << endl;
        return os;
    }

    练习7.7

    int main(int argc, char* argv[])
    {   
        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?!" << endl;
            return -1;
        }
        return 0;
    }

    练习7.8

    print应该是常量引用的,因为我们不需要修改传入值

    练习7.9

    struct Person {
        string name;
        string address;
        const string isname() { return name; }
        const string isaddress() { return address; }
    };
    
    istream& read(istream& is, Person& it) {
        is >> it.name >> it.address;
        return is;
    }
    
    ostream& print(ostream& os, Person& it) {
        os << it.isname() << " " << it.isaddress() << endl;
        return os;
    }

    练习7.10

    判断读操作对象是否符合要求

    练习7.11

    struct Sales_data {
        string bookNo;
        unsigned units_sold = { 0 };
        double revenue = { 0.0 };
    
        string isbn() { return bookNo; }
        Sales_data& combine(const Sales_data& rhs) {
            units_sold += rhs.units_sold;
            revenue += rhs.revenue;
            return *this;
        }
        Sales_data(string bn, unsigned us, double re) :bookNo(bn), units_sold(us), revenue(re) {}
        Sales_data() = default;
    };
    int main(int argc, char* argv[])
    {   
        Sales_data total("",0,0);
        print(cout, total);
        return 0;
    }

    练习7.12

    struct Sales_data {
        string bookNo;
        unsigned units_sold = { 0 };
        double revenue = { 0.0 };
    
        string isbn() { return bookNo; }
        Sales_data& combine(const Sales_data& rhs) {
            units_sold += rhs.units_sold;
            revenue += rhs.revenue;
            return *this;
        }
        Sales_data(string bn, unsigned us, double re) :bookNo(bn), units_sold(us), revenue(re) {}
        Sales_data() = default;
        Sales_data(istream& is) { read(is, *this); }
    };

    练习7.13

    int main(int argc, char* argv[])
    {
        Sales_data total(cin);
        if (cin) {
            Sales_data trans(cin);
            do{
                if (total.bookNo == trans.bookNo) {
                    total.revenue += trans.revenue;
                    total.units_sold += trans.units_sold;
                }
                else {
                    cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
                    total = trans;
                }
            } while (read(cin, trans));
            cout << total.bookNo << " " << total.units_sold << " " << total.revenue << endl;
        }
        else {
            std::cerr << "No Data?!" << endl;
            return -1;
        }
        return 0;
    }

    练习7.14

    Sales_data(string bn, unsigned us, double re) :bookNo(bn), units_sold(us = 0), revenue(re = 0.0) {};

    练习7.15

    struct Person {
        string name;
        string address;
        const string isname() { return name; }
        const string isaddress() { return address; }
        Person() = default;
        Person(string name, string address) :name(name), address(address) {}
    };

    练习7.16

    没有限定。在整个程序内可被访问到的成员应定义在public说明符之后;可以被类的成员访问但不能被使用该类代码访问的应该定义在private说明符之后。

    练习7.17

    有区别,struct的默认访问权限是public,class的默认访问权限是class

    练习7.18

    封装:使用函数指针把属性与方法封装到结构体中

    练习7.19

    成员函数声明成public,成员变量声明成private

    练习7.20

    类的友元函数是定义在类外部,但有权访问类的所有私有(private)成员和保护(protected)成员。尽管友元函数的原型有在类的定义中出现过,但是友元函数并不是成员函数。

    优点:能访问私有成员

    缺点:破坏封装性

    练习7.21

    class Sales_data {
        friend istream& read(istream& is, Sales_data& it);
        friend ostream& print(ostream& os, Sales_data& it);
    private:
        string bookNo;
        unsigned units_sold = { 0 };
        double revenue = { 0.0 };
    public:
        const string& isbn() { return bookNo; }
        Sales_data& combine(const Sales_data& rhs) {
            units_sold += rhs.units_sold;
            revenue += rhs.revenue;
            return *this;
        }
        Sales_data(string bn, unsigned us, double re) :bookNo(bn), units_sold(us = 0), revenue(re = 0.0) {};
        Sales_data() = default;
        Sales_data(istream& is) { read(is, *this); }
    };
    
    Sales_data add(Sales_data& lhs, Sales_data& rhs) {
        Sales_data sum = lhs;
        sum.combine(rhs);
        return sum;
    }
    
    istream &read(istream& is, Sales_data& it) {
        is >> it.bookNo >> it.units_sold >> it.revenue;
        return is;
    }
    
    ostream& print(ostream& os, Sales_data& it) {
        os << it.isbn() << " " << it.units_sold << " " << it.revenue << endl;
        return os;
    }

    练习7.22

    class Person {
        friend istream& read(istream& is, Person& it);
        friend ostream& print(ostream& os, Person& it);
    private:
        string name;
        string address;
    public:
        const string isname() { return name; }
        const string isaddress() { return address; }
        Person() = default;
        Person(string name, string address) :name(name), address(address) {}
    };
    
    istream& read(istream& is, Person& it) {
        is >> it.name >> it.address;
        return is;
    }
    
    ostream& print(ostream& os, Person& it) {
        os << it.isname() << " " << it.isaddress() << endl;
        return os;
    }

    练习7.23

    #ifndef SCREEN_H_
    #define SCREEN_H_
    #include<string>
    class Screen {
    public:
        using pos = std::string::size_type;
    private:
        pos cursor = 0;
        pos height = 0, width = 0;
        std::string contents;
    };
    #endif // !SCREEN_H_

    练习7.24

    class Screen {
    public:
        using pos = std::string::size_type;
        Screen() = default;
        //Screen(pos ht,pos wd):height(ht),width(wd),contents(ht*wd,' '){}
        Screen(pos ht, pos wd,char c=' ') :height(ht), width(wd), contents(ht* wd, c) {}
    private:
        pos cursor = 0;
        pos height = 0, width = 0;
        std::string contents;
    };

    练习7.25

    可以,因为只有内置类型和可变类型如vector类型,string类型可以依赖于操作的默认版本

    练习7.26

    在函数体外定义或者类内声明的返回值前添加inline关键字即可

    练习7.27

    #ifndef SCREEN_H_
    #define SCREEN_H_
    #include<string>
    class Screen {
    public:
        using pos = std::string::size_type;
        Screen() = default;
        //Screen(pos ht,pos wd):height(ht),width(wd),contents(ht*wd,' '){}
        Screen(pos ht, pos wd,char c=' ') :height(ht), width(wd), contents(ht* wd, c) {}
        Screen& set(char);
        Screen& set(pos, pos,char);
        Screen& move(pos, pos);
        Screen& display(std::ostream& os);
        const Screen& display(std::ostream& os)const;
    private:
        pos cursor = 0;
        pos height = 0, width = 0;
        std::string contents;
        void do_display(std::ostream& os)const {
            os << contents;
        }
    };
    inline Screen& Screen::set(char c) {
        contents[cursor] = c;
        return *this;
    }
    inline Screen& Screen::set(pos row, pos col,char c) {
        cursor = row * width + col;
        contents[row * width + col] = c;
        return *this;
    }
    inline Screen& Screen::move(pos row, pos col) {
        char newc = contents[cursor];
        contents[cursor] = contents[row * width + col];
        contents[row * width + col] = newc;
        return *this;
    }
    inline Screen& Screen::display(std::ostream& os)
    {
        do_display(os);
        return *this;
    }
    inline const Screen& Screen::display(std::ostream& os) const
    {
        do_display(os);
        return *this;
    }
    #endif // !SCREEN_H_

    练习7.28

    若函数返回类型变为Screen,则返回的是对象的副本,函数的操作只能添加于对象的副本上,对象的本身并没有改变。所以两次显示结果会不一样,myScreen不会输出#

    练习7.29

    #xxxxxxxxxxxxxxxxxxxxxxxx
    xxxxxxxxxxxxxxxxxxxxxxxxx

    推测正确

    练习7.30

    1:当需要将一个对象作为整体引用而不是引用对象的一个成员时,使用this,则该函数返回对调用该函数的对象的引用。 

    2:可以非常明确地指出访问的是调用该函数的对象的成员,且可以在成员函数中使用与数据成员同名的形参。  

    缺点:不必要使用,代码多余。

    练习7.31

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

    练习7.32

    class Screeen;
    
    class Window_mgr
    {
    public:
        using ScreenIndex=std::vector<Screen>::size_type ;
        void clear(ScreenIndex);
    private:
        std::vector<Screen> screens;
    };
    #include "Window_mgr.h"
    class Screen {
    public:
        friend void Window_mgr::clear(ScreenIndex);
        using pos = std::string::size_type;
        Screen() = default;
        //Screen(pos ht,pos wd):height(ht),width(wd),contents(ht*wd,' '){}
        Screen(pos ht, pos wd,char c=' ') :height(ht), width(wd), contents(ht* wd, c) {}
        Screen set(char);
        Screen set(pos, pos,char);
        Screen move(pos, pos);
        Screen display(std::ostream& os);
        const Screen display(std::ostream& os)const;
    private:
        pos cursor = 0;
        pos height = 0, width = 0;
        std::string contents;
        void do_display(std::ostream& os)const {
            os << contents;
        }
    };
    
    inline Screen Screen::set(char c) {
        contents[cursor] = c;
        return *this;
    }
    inline Screen Screen::set(pos row, pos col,char c) {
        cursor = row * width + col;
        contents[row * width + col] = c;
        return *this;
    }
    inline Screen Screen::move(pos row, pos col) {
        char newc = contents[cursor];
        contents[cursor] = contents[row * width + col];
        contents[row * width + col] = newc;
        return *this;
    }
    inline Screen Screen::display(std::ostream& os)
    {
        do_display(os);
        return *this;
    }
    inline const Screen Screen::display(std::ostream& os) const
    {
        do_display(os);
        return *this;
    }
    inline void Window_mgr::clear(ScreenIndex i)
    {
        Screen& s = screens[i];
        s.contents = std::string(s.height * s.width, ' ');
    }

    练习7.33

    C++ 成员声明中不允许限定名

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

    练习7.34

    Screen类的成员声明中出现pos且在pos的typedef之前的的一律报错。

    练习7.35

    typedef string Type;
    Type initval();//外层作用域Money
    class Exercise {
    public:
        typedef double Type;//错误;不能重新定义Money
        Type setVal(Type);//Type类内匹配
        Type initVal();//Type类内匹配,在类内重新声明initVal函数
    private:
        int val;
    };
    Type Exercise::setVal(Type parm) {//外层作用域Type
        val = parm + initVal();
        return val;
    }

    修改后

    typedef string Type;
    Type initval();
    class Exercise {
    public:
        typedef double Double_type;//修改类内的类型名与外部作用域类型名不相同
        Double_type setVal(Double_type);
        Double_type initVal();
    private:
        int val;
    };
    Exercise::Double_type Exercise::setVal(Double_type parm) {
        val = parm + initVal();
        return val;
    }

    练习7.36

    struct X
    {
        X(int i, int j) :base(i), rem(base% j) {}//试图使用未初始化的base来初始化rem
        int rem, base;
    };

    更改成员出现顺序来更改成员的初始化顺序

    struct X
    {
        X(int i, int j) :base(i), rem(base% j) {}
        int base, rem;
    };

    练习7.37

    Sales_data first_item(cin);//Sales_data(std::istream &is){read(is,*this) };与传入的值相关
    
    int main(int argc, char* argv[])
    {
        Sales_data next;//Sales_data(string s="") :bookNo(s){};cnt = 0, revenue = 0.0
        Sales_data last("9-999-99999-9");//Sales_data(string bn, unsigned cnt, double rev) :bookNo(bn), units_sold(cnt), revenue(cnt*rev) {};bookNo = "9-999-99999-9", cnt = 0, revenue = 0.0
    }

    练习7.38

    Sales_data(istream& is = std::cin) { read(is, *this); }

    练习7.39

    不合法,这样的话接受string的构造函数和接受istream&的构造函数都可以接受无参函数,编译器无法判断要调用哪个函数

    练习7.40

    #ifndef TREE_H_
    #define TREE_H_
    #include <vector>
    #include <string>
    /*每棵树上都有很多分叉,每个分叉上都可能长着很多片叶子,
    所以用Tree来描述树以及树上的枝干,用string来代表每一个枝干上的叶子
    */
    
    class Tree
    {
    public:
        using leaf = std::string;
        Tree(std::vector<Tree> t, std::vector<leaf> l) : trees{ t }, leaves{ l } { }
        Tree(std::vector<Tree> t, leaf l) : trees{ t }, leaves{ l } { }
        Tree(Tree t, std::vector<leaf> l) : trees{ t }, leaves{ l } { }
        Tree(Tree t, leaf l) : trees{ t }, leaves{ l } { }
        void setTree(Tree t);
        void setTrees(std::vector<Tree> t);
        void setLeaf(leaf l);
        void setLeaves(std::vector<leaf> l);
        std::vector<Tree> getTrees();
        std::vector<leaf> getLeaves();
    private:
        std::vector<Tree> trees;
        std::vector<leaf> leaves;
    };
    #endif // !TREE_H_

    练习7.41

    Sales_data(string bn, unsigned us = 0, double re = 0.0) :bookNo(bn), units_sold(us), revenue(re) { std::cout << "Sales_data(string bn, unsigned us = 0, double re = 0.0)" << std::endl;}
    Sales_data() :Sales_data("") { std::cout << "Sales_data()" << std::endl; }
    Sales_data(istream& is) :Sales_data() { read(is, *this); std::cout << "Sales_data(istream& is)" << std::endl; }
    Sales_data first_item(cin);
    Sales_data next;
    Sales_data last("9-999-99999-9");
    Sales_data(string bn, unsigned us = 0, double re = 0.0)
    Sales_data()
    1 1 1
    Sales_data(istream& is)
    Sales_data(string bn, unsigned us = 0, double re = 0.0)
    Sales_data()
    Sales_data(string bn, unsigned us = 0, double re = 0.0)

    练习7.42

    class Tree
    {
    public:
        using leaf = std::string;
        Tree(std::vector<Tree> t, std::vector<leaf> l) : trees(t), leaves(l) { }
        Tree(std::vector<Tree> t, leaf l) : Tree(t, std::vector<leaf>{l}) { }
        Tree(Tree t, std::vector<leaf> l) : Tree(std::vector<Tree>{t}, l) { }
        Tree(Tree t, leaf l) : Tree(std::vector<Tree>{t}, std::vector<leaf>{l}) { }
        void setTree(Tree t);
        void setTrees(std::vector<Tree> t);
        void setLeaf(leaf l);
        void setLeaves(std::vector<leaf> l);
        std::vector<Tree> getTrees();
        std::vector<leaf> getLeaves();
    private:
        std::vector<Tree> trees;
        std::vector<leaf> leaves;
    };

    练习7.43

    class NoDefault
    {
    public:
        NoDefault(int x) {};
    private:
    
    };
    class C
    {
    public:
        C() :noDefault(0) {};
    private:
        NoDefault noDefault;
    };

    练习7.44

    不合法,因为NoDefault类没有默认构造函数,所以不能无参初始化

    练习7.45

    合法,因为C类有默认构造函数,而且能对成员类初始化

    练习7.46

    (a)不正确,如果类不提供构造函数,编译器会自动创建一个合成的默认构造函数

    (b)不正确,当对象被默认初始化或值初始化都自动执行默认构造函数

    (c)不正确,值初始化也需要类提供默认构造函数

    (d)不正确,类没有定义任何构造函数的时候,编译器就会自动生成一个默认构造函数

    练习7.47

    应该。优点是可以避免隐式的类类型转换带来的风险,生成一个隐式转换后的类临时变量,完成操作后就消失了。缺点是用户使用时需要显式的创建临时对象,对用户提高了要求

    练习7.48

    string null_isbn("9-99-9999-9");
    Sales_data item1(null_isbn);        // 用string类型的null_isbn直接初始化item1
    Sales_data item2("9-99-9999-9");    // 用字符串字面值初始化item2.

    练习7.49

    Sales_data &combine(Sales_data); // 正常初始化,将s转化成Sales_data类型。
    Sales_data &combine(Sales_data&);// 报错,string不能转化为Sales_data类型的引用。
    Sales_data &combine(const Sales_data&) const;//报错,常量函数不允许对值做出改变。

    练习7.50

    explicit Person(istream& in) { read(in, *this); }//只有该构造函数是传入一个参数的

    练习7.51

    string存在const char*的隐式转换,而vector如果允许隐式转换则会提高理解难度

    练习7.52

    聚合类的原理(编译器遇到Plain OI' Data声明形式由于与C程序兼容,所以可以使用C程序形式进行初始化,从C++20开始, POD这一概念就被废止, 取而代之的是一系列更为精确的定义, 如TrivialType

    要使用下面这种方式初始化则必然是聚合类,但是聚合类要求没有类内初始值,所以应该修改为

    struct Sales_data
    {
        std::string bookNo;
        unsigned units_sold;
        double revenue;
    };

    练习7.53

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

    练习7.54

    constexpr函数的返回值和所有形参的类型都必须得是字面值类型,而且函数体中必须有且只有一条return语句,可包含其他语句但不能在运行期起作用,例如;空语句等

    所以可以被声明为constexpr

    练习7.55

    聚合类是字面值常量类,因为聚合类初始化必须使用字面值列表来进行初始化

    练习7.56

    和类本身相关但不是和类的对象保持联系的成员是类的静态成员,在成员声明前加上static关键字

    优点:和类关联但不和类的对象关联,一旦修改所有对象都能使用新值

    静态成员和普通成员的区别:不是在创建类的对象时被定义,而是在类的外部定义和初始化,因此静态数据成员可以是不完全类型。类的静态成员属于类本身,在类加载时就会分配内存,可以通过类名直接进行访问。普通成员属于类的对象,只有在类对象产生时才会分配内存。只能通过对象去访问。

    练习7.57

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

    练习7.58

    static double rate = 6.5;//成员本身不是常量表达式
    static const int vecSize = 20;
    static std::vector<double> vec(vecSize);//vector对象不能在类内初始化
  • 相关阅读:
    电脑与欧姆龙plc通过网络通信
    photometric_stereo halcon光度立体法三维表面重建
    WPF实现放大镜
    备忘
    Halcon模板匹配
    C# Halcon联合编程问题(二)
    C# Halcon混合编程中遇到的问题(一)
    各个平台的解释
    python数据结构的性能测试
    docker container里面为什么不用装OS
  • 原文地址:https://www.cnblogs.com/GodZhuan/p/13910343.html
Copyright © 2020-2023  润新知