美文网首页C++ Primer Plus习题及答案
C++ Primer Plus习题及答案-第十八章

C++ Primer Plus习题及答案-第十八章

作者: 艰默 | 来源:发表于2023-02-19 09:08 被阅读0次

    习题选自:C++ Primer Plus(第六版)
    内容仅供参考,如有错误,欢迎指正 !

    C++ decltype和返回类型后置

    左右值引用和移动语义

    C++11 新的类功能

    C++11 Lambda表达式

    C++11 包装器function

    复习题

    1. 使用用大括号括起的初始化列表语法重写下述代码。重写后的代码不应使用数组ar:

    class Z200
    {
        private:
        int j;
        char ch;
        double z;
        public:
        Z200(int jv, char chv, zv) : j(jv), ch(chv), z(zv) {}
        ...
    };
    double x = 8.8;
    std::string s = "What a bracing effect!";
    int k(99);
    Z200 zip(200,'Z',0.675);
    std::vector<int> ai(5);
    int ar[5] = {3, 9, 4, 7, 1};
    for (auto pt = ai.begin(), int i = 0; pt != ai.end(); ++pt, ++i)
        *pt = ai[i];
    

    重写后代码:

    class Z200
    {
        private:
        int j;
        char ch;
        double z;
        public:
        Z200(int jv, char chv, zv) : j(jv), ch(chv), z(zv) {}
        ...
    };
    double x{8.8}; // = {8.8}
    std::string s{"What a bracing effect!"};
    int k{99};
    Z200 zip{200,'Z',0.675};
    std::vector<int> ai{3, 9, 4, 7, 1};
    

    2.2. 在下述简短的程序中,哪些函数调用不对?为什么?对于合法的函数调用,指出其引用参数指向的是什么。

    #include <iostream>
    using namespace std;
    double up(double x) { return 2.0* x;}
    void r1(const double &rx) {cout << rx << endl;}
    void r2(double &rx) {cout << rx << endl;}
    void r3(double &&rx) {cout << rx << endl;}
    int main()
    {
        double w = 10.0;
        r1(w);
        r1(w+1);
        r1(up(w));
        r2(w);
        r2(w+1);
        r2(up(w));
        r3(w);
        r3(w+1);
        r3(up(w));
        return 0;
    }
    
    • r1(w); ----合法,形参rx指向w

    • r1(w+1);----合法,形参rx指向一个临时变量,这个变量被初始化为w+1

    • r1(up(w));---合法,形参rx指向一个临时变量,这个变量被初始化为up(w)的返回值。

    • r2(w);---合法,形参rx指向w

    • r2(w+1); ---非法,因为w+1是一个右值。

    • r2(up(w));---非法,因为up(w)的返回值是一个右值。

    • r3(w);---非法,因为右值引用不能指向左值(如w)。

    • r3(w+1);---合法,rx指向表达式w+1的临时拷贝。

    • r3(up(w));---合法,rx指向up(w)的临时返回值。

    一般而言,将左值传递给const左值引用参数的时候,参数将被初始化为左值。将右值传递给函数时,const左值引用参数将指向右值的临时拷贝。将左值传递给非const左值引用参数时,参数将被初始化为左值;但非const左值形参不能接受右值实参。

    3. a. 下述简短的程序显示什么?为什么?

    #include <iostream>
    using namespace std;
    double up(double x) { return 2.0 * x; }
    void r1(const double &rx) { cout << "const double & rx\n"; }
    void r1(double &rx) { cout << "double & rx\n"; }
    int main() {
        double w = 10.0;
        r1(w);
        r1(w + 1);
        r1(up(w));
        return 0;
    }
    

    b. 下述简短的程序显示什么?为什么?

    #include <iostream>
    using namespace std;
    double up(double x) { return 2.0 * x; }
    void r1(double &rx) { cout << "double & rx\n"; }
    void r1(double &&rx) { cout << "double && rx\n"; }
    int main() {
        double w = 10.0;
        r1(w);
        r1(w + 1);
        r1(up(w));
        return 0;
    }
    

    c. 下述简短的程序显示什么?为什么?

    #include <iostream>
    using namespace std;
    double up(double x) { return 2.0 * x; }
    void r1(const double &rx) { cout << "const double & rx\n"; }
    void r1(double &&rx) { cout << "double && rx\n"; }
    int main() {
        double w = 10.0;
        r1(w);
        r1(w + 1);
        r1(up(w));
        return 0;
    }
    

    a.

    double & rx
    const double & rx
    const double & rx
    

    const左值引用与左值实参匹配,因此r1(w);调用void r1(double &rx)。另外两个实参均为右值,const左值引用可以指向他们的拷贝。【将右值传递给函数时,const左值引用参数将指向右值的临时拷贝。】。

    b.

    double & rx
    double && rx
    double && rx
    

    左值引用与左值实参w匹配,而右值引用与两个右值实参匹配。

    c.

    const double & rx
    double && rx
    double && rx
    

    const左值引用与左值实参w匹配,而右值引用与两个右值实参匹配。

    总之,非const左值形参与左值实参匹配,非cosnt右值形参与右值实参匹配;const左值形参可以与左值或右值实参匹配。如果可供选择的话,编译器优先选择前两种方式。

    4. 哪些成员函数是特殊的成员函数?它们特殊的原因是什么?

    特殊成员函数:默认构造函数、复制构造函数、移动构造函数、析构函数、复制赋值运算符和移动赋值运算符。这些函数之所以特殊,是因为编译器将根据情况自动提供它们的默认版本。

    5. 假设Fizzle类只有如下所示的数据成员:

    class Fizzle
    {
        private:
        double bubbles[4000];
        ...
    };
    

    为什么不适合给这个类定义移动构造函数?要让这个类适合定义移动构造函数,应如何修改存储4000个double值的方式?

    移动构造函数是在转让数据所有权可行的时候是合适的。但对于标准数组没有转让所有权的机制,因此不适合给该类定义移动构造函数。如果Fizzle使用指针和动态内存分配来存储这4000个double值,即可以将数据的地址赋给新指针,以转让其所有权,则适合给Fizzle定义移动构造函数。

    6. 修改下述简短的程序,使其使用lambda表达式而不是f1( )。请不要修改show2( )。

    #include <iostream>
    template<typename T>
    void show2(double x, T& fp) {std::cout << x << " -> " << fp(x) << '\n';}
    double f1(double x) { return 1.8*x + 32;}
    int main()
    {
        show2(18.0, f1);
        return 0;
    }
    

    修改后:

    #include <iostream>
    template <typename T>
    void show2(double x, T& fp) { std::cout << x << " -> " << fp(x) << '\n';}
    //void show2(double x, T fp) {std::cout << x << " -> " << fp(x) << '\n';}
    int main() {
        auto f2 = [](double x) { return 1.8 * x + 32; };
        show2(18.0, f2);
        //show2(18.0, [](double x){return 1.8*x + 32;});
        return 0;
    }
    

    7. 修改下述简短而丑陋的程序,使其使用lambda表达式而不是函数符Adder。请不要修改sum( )。

    #include <iostream>
    #include <array>
    const int Size = 5;
    template<typename T>
    void sum(std::array<double,Size> a, T& fp);
    class Adder
    {
        double tot;
        public:
        Adder(double q = 0) : tot(q) {}
        void operator()(double w) { tot +=w;}
        double tot_v () const {return tot;};
    };
    int main()
    {
        double total = 0.0;
        Adder ad(total);
        std::array<double, Size> temp_c = {32.1, 34.3, 37.8, 35.2, 34.7};
        sum(temp_c,ad);
        total = ad.tot_v();
        std::cout << "total: " << ad.tot_v() << '\n';
        return 0;
    }
    template<typename T>
    void sum(std::array<double,Size> a, T& fp)
    {
        for(auto pt = a.begin(); pt != a.end(); ++pt)
        {
            fp(*pt);
        }
    }
    

    修改后:

    #include <array>
    #include <iostream>
    const int Size = 5;
    template <typename T>
    void sum(std::array<double, Size> a, T& fp);
    
    int main() {
        double total = 0.0;
        std::array<double, Size> temp_c = {32.1, 34.3, 37.8, 35.2, 34.7};
        auto f = [&total](double x) { total += x; };
        sum(temp_c, f);
        std::cout << "total: " << total << '\n';
        return 0;
    }
    template <typename T>
    void sum(std::array<double, Size> a, T& fp) {
        for (auto pt = a.begin(); pt != a.end(); ++pt) {
            fp(*pt);
        }
    }
    

    编程练习

    1. 下面是一个简短程序的一部分:

    int main()
    {
        using namespace std;
        // list of double deduced from list contents
        auto q = average_list({15.4, 10.7, 9.0});
        cout << q << endl;
        // list of int deduced from list contents
        cout << average_list({20, 30, 19, 17, 45, 38} ) << endl;
        // forced list of double
        auto ad = average_list<double>({'A', 70, 65.33});
        cout << ad << endl;
        return 0;
    }
    

    请提供函数average_list( ),让该程序变得完整。它应该是一个模板函数,其中的类型参数指定了用作函数参数的initilize_list模板的类型以及函数的返回类型。

    修改后:

    #include <algorithm>
    #include <initializer_list>
    #include <iostream>
    using namespace std;
    
    template <typename T>
    T average_list(initializer_list<T> l) {
        T sum = 0;
        if (l.size() == 0) return 0;
        for_each(l.begin(), l.end(), [&sum](T t) { sum += t; });
        return sum / l.size();
    }
    
    int main() {
        using namespace std;
        // list of double deduced from list contents
        auto q = average_list({15.4, 10.7, 9.0});
        cout << q << endl;
        // list of int deduced from list contents
        cout << average_list({20, 30, 19, 17, 45, 38}) << endl;
        // forced list of double
        auto ad = average_list<double>({'A', 70, 65.33});
        cout << ad << endl;
        return 0;
    }
    

    2. 下面是类Cpmv的声明:

    class Cpmv
    {
        public:
        struct Info
        {
            std::string qcode;
            std::string zcode;
        };
        private:
        Info *pi;
        public:
        Cpmv();
        Cpmv(std::string q, std::string z);
        Cpmv(const Cpmv & cp);
        Cpmv(Cpmv && mv);
        ~Cpmv();
        Cpmv & operator=(const Cpmv & cp);
        Cpmv & operator=(Cpmv && mv);
        Cpmv operator+(const Cpmv & obj) const;
        void Display() const;
    };
    

    函数operator+ ( )应创建一个对象,其成员qcode和zcode有操作数的相应成员拼接而成。请提供为移动构造函数和移动赋值运算符实现移动语义的代码。编写一个使用所有这些方法的程序。为方便测试,让各个方法都显示特定的内容,以便知道它们被调用。

    代码如下:

    #include <iostream>
    using namespace std;
    
    class Cpmv {
        public:
        struct Info {
            std::string qcode;
            std::string zcode;
        };
    
        private:
        Info *pi;
    
        public:
        Cpmv();
        Cpmv(std::string q, std::string z);
        Cpmv(const Cpmv &cp);
        Cpmv(Cpmv &&mv);
        ~Cpmv();
        Cpmv &operator=(const Cpmv &cp);
        Cpmv &operator=(Cpmv &&mv);
        Cpmv operator+(const Cpmv &obj) const;
        void Display() const;
    };
    
    int main() {
        Cpmv c1;
        Cpmv c2("abc", "123");
        Cpmv c3(c2);
        c1 = c2;
        c1.Display();
    
        Cpmv c4(move(c1));
        c4.Display();
    
        Cpmv c5;
        c5 = move(c2);
        c5.Display();
    
        return 0;
    }
    
    Cpmv::Cpmv() {
        pi = new Info;
        pi->qcode = "";
        pi->zcode = "";
        cout << "Cpmv() called.\n";
    }
    
    Cpmv::Cpmv(std::string q, std::string z) {
        pi = new Info;
        pi->qcode = q;
        pi->zcode = z;
        cout << "Cpmv(std::string q, std::string z) called.\n";
    }
    
    Cpmv::Cpmv(const Cpmv &cp) {
        pi = new Info;
        pi->qcode = cp.pi->qcode;
        pi->zcode = cp.pi->zcode;
        cout << "Cpmv(const Cpmv &cp) called.\n";
    }
    
    Cpmv::Cpmv(Cpmv &&mv) {
        pi = mv.pi;
        mv.pi = nullptr;
        cout << "Cpmv(Cpmv &&mv) called.\n";
    }
    
    Cpmv::~Cpmv() {
        delete pi;
        cout << "~Cpmv()  called.\n";
    }
    
    Cpmv &Cpmv::operator=(const Cpmv &cp) {
        cout << "Cpmv &operator=(const Cpmv &cp) called.\n";
        if (this == &cp) {
            return *this;
        }
        delete pi;
        pi = new Info;
        pi->qcode = cp.pi->qcode;
        pi->zcode = cp.pi->zcode;
        return *this;
    }
    
    Cpmv &Cpmv::operator=(Cpmv &&mv) {
        cout << "Cpmv &operator=(Cpmv &&mv) called.\n";
        if (this == &mv) {
            return *this;
        }
        delete pi;
        pi = mv.pi;
        mv.pi = nullptr;
        return *this;
    }
    
    Cpmv Cpmv::operator+(const Cpmv &obj) const {
        cout << "Cpmv operator+(const Cpmv &obj) called.\n";
        Cpmv cv;
        cv.pi->qcode = this->pi->qcode + obj.pi->qcode;
        cv.pi->zcode = this->pi->zcode + obj.pi->zcode;
        return cv;
    }
    
    void Cpmv::Display() const {
        cout << "The qcode is " << this->pi->qcode << endl;
        cout << "The zcode is " << this->pi->zcode << endl;
    }
    

    3. 编写并测试可变参数模板函数sum_value( ),它接受任意长度的参数列表(其中包含数值,但可以是任何类型),并以long double的方式返回这些数值的和。

    main.cpp:

    #include <iostream>
    #include <string>
    
    // definition for 1 parameter
    template <typename T>
    long double sum_value(const T& value) {
      return value;
    }
    
    // definition for 2 or more parameters
    template <typename T, typename... Args>
    long double sum_value(const T& value, const Args&... args) {
      return value + sum_value(args...);
    }
    
    int main() {
      int n = 14;
      double x = 2.71828;
      std::cout << sum_value(n, x, 'a') << std::endl;
      return 0;
    }
    

    4. 使用lambda重新编写程序清单16.15。具体地说,使用一个有名称的lambda替换函数outint( ),并将函数符替换为两个匿名lambda表达式。

    main.cpp:

    #include <algorithm>
    #include <iostream>
    #include <iterator>
    #include <list>
    
    auto outint_l = [](int n) { std::cout << n << " "; };
    
    int main() {
        using std::cout;
        using std::endl;
        using std::list;
        int vals[10] = {50, 100, 90, 180, 60, 210, 415, 88, 188, 201};
        list<int> yadayada(vals, vals + 10);  // range constructor
        list<int> etcetera(vals, vals + 10);
        // C++11 can use the following instead
        // list<int> yadayada = {50, 100, 90, 180, 60, 210, 415, 88, 188, 201};
        // list<int> etcetera {50, 100, 90, 180, 60, 210, 415, 88, 188, 201};
        cout << "Original lists:\n";
        for_each(yadayada.begin(), yadayada.end(), outint_l);
        cout << endl;
        for_each(etcetera.begin(), etcetera.end(), outint_l);
        cout << endl;
        yadayada.remove_if(
            [](int n) { return n > 100; });  // use a named function object
        etcetera.remove_if(
            [](int n) { return n > 200; });  // construct a function object
        cout << "Trimmed lists:\n";
        for_each(yadayada.begin(), yadayada.end(), outint_l);
        cout << endl;
        for_each(etcetera.begin(), etcetera.end(), outint_l);
        cout << endl;
        return 0;
    }
    

    相关文章

      网友评论

        本文标题:C++ Primer Plus习题及答案-第十八章

        本文链接:https://www.haomeiwen.com/subject/yarikdtx.html