C++ Primier Plus(第六版) 第十八章 探讨C++新标准编程练习答案
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;
}
请提供函数average_list(),让该程序变得完整。他应该是一个模板函数,其中的类型参数指定了用作函数参数的initilize_list模板的类型以及函数的返回类型。
本题刚开始笔者想复杂了,计划使用可变参数模板来实现,后面发现实现不了,后面发现本题考查的是initialize_lizer_list类的使用,程序如下:
// pe1.cpp -- use initializer_list
#include <iostream>
#include <initializer_list>
template<typename T>
T average_list(std::initializer_list<T> il);
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;
}
template<typename T>
T average_list(std::initializer_list<T> il)
{
int i = 0;
T sum = 0;
for (auto pil = il.begin(); pil != il.end(); pil++, i++)
{
sum += *pil;
}
return sum / i;
}
运行结果如下:
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有操作数的相应成员拼接而成。其提供为移动构造函数和移动赋值运算符实现移动语义的代码。编写一个使用所有这些方法的程序。为方便测试,让各个方法都显示特定的内容,以便直到它们被调用。
本题不算难,考查的是移动复制构造函数和移动赋值函数的定义。调用移动复制函数的过程出现了问题,没有自动完成,因此采用了强制移动。程序如下:
// cpmv.h -- prototype Cpmv class
#ifndef CPMV_H_
#define CPMV_H_
#include <iostream>
#include <string>
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;
};
#endif
// cpmv.cpp -- methods for Cpmv class
#include "cpmv.h"
Cpmv::Cpmv()
{
std::cout << "Called Cpmv()\n";
pi = new Info;
}
Cpmv::Cpmv(std::string q, std::string z)
{
std::cout << "Called Cpmv(string q, string z)\n";
pi = new Info;
pi->qcode = q;
pi->zcode = z;
}
Cpmv::Cpmv(const Cpmv & cp)
{
std::cout << "Called Cpmv(const Cpmv & cp)\n";
pi = new Info;
pi->qcode = cp.pi->qcode;
pi->zcode = cp.pi->zcode;
}
Cpmv::Cpmv(Cpmv && mv)
{
std::cout << "Called Cpmv(Cpmv && mv)\n";
pi = new Info;
pi = mv.pi;
mv.pi = nullptr;
}
Cpmv::~Cpmv()
{
std::cout << "Called ~Cpmv()\n";
delete pi;
}
Cpmv & Cpmv::operator=(const Cpmv & cp)
{
std::cout << "Called operator=(const Cpmv & cp)\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)
{
std::cout << "Called operator=(Cpmv && mv)\n";
if (this == &mv)
return *this;
delete pi;
pi = new Info;
pi = mv.pi;
mv.pi = nullptr;
return *this;
}
Cpmv Cpmv::operator+(const Cpmv & obj) const
{
std::cout << "Called operator+(const Cpmv & obj) const\n";
Cpmv temp;
temp.pi->qcode = pi->qcode + obj.pi->qcode;
temp.pi->zcode = pi->zcode + obj.pi->zcode;
return temp;
}
void Cpmv::Display() const
{
std::cout << "Called Display() const\n";
std::cout << pi->qcode << '\t';
std::cout << pi->zcode << '\n';
}
// test_cpmv.cpp -- test the class cpmv
#include "cpmv.h"
#include <utility>
int main()
{
{
// using Cpmv(std::string q, std::string z)
Cpmv one("C++", "Fight!");
Cpmv two(", I love!",", go go go!");
one.Display();
two.Display();
// using Cpmv(Cpmv && mv) and operator+(const Cpmv & obj)
std::cout << "\nthree(one + two): ";
Cpmv three(std::move(one + two));
three.Display();
// using Cpmv(const Cpmv & cp)
Cpmv four(one);
four.Display();
// operator=(Cpmv && mv) and operator+(const Cpmv & obj)
std::cout << "\none = four + two: ";
one = four + two;
one.Display();
// operator=(const Cpmv & mv)
two = three;
two.Display();
}
std::cout << "Done.\n";
return 0;
}
运行结果如下:
3.编写并测是可变参数模板函数sum_value(),它接受任何长度的参数列表(其中包含数值,但也可以是任何类型),并以long double的方式返回这些数值的和。
本题考查的是可变模板参数:任何类型实际上是有条件的,基本类型,c-字符串类型本程序不支持,支持的类型是定义了+运算符的数据类型,程序如下:
// pe3.cpp -- variadic template
#include <iostream>
#include <iomanip>
template<typename T, typename... Args>
long double sum_value(T value, Args... args);
long double sum_value() { return 0.0; }
int main()
{
using std::cout;
// list of int and double and char
auto q = sum_value(1, 2, 9.6, 'a', 10);
cout << std::fixed << std::setprecision(4);
cout << q << std::endl;
auto sum = sum_value('!', 345, 333,'A');
cout << sum << std::endl;
return 0;
}
template<typename T, typename... Args>
long double sum_value(T value, Args... args)
{
return value + sum_value(args...);
}
运行结果如下:
4. 使用lambda重新编写程序清单16.15。具体的说,使用一个有名称的lambda函数替换函数outint(),并将函数符替换为两个匿名的lambda表达式。
本题考查的是有名称的lambda函数和匿名lambda表达式的定义,注意有名称的lambda函数使用
auto function-name = [](typename x){...},程序如下:
// pe4.cpp -- use lambda function
#include <iostream>
#include <list>
#include <iterator>
#include <algorithm>
// has name lambda function
auto outint = [](int n) { std::cout << n << " "; };
int main()
{
using std::list;
using std::cout;
using std::endl;
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 list:\n";
for_each(yadayada.begin(), yadayada.end(), outint);
cout << endl;
for_each(etcetera.begin(), etcetera.end(), outint);
cout << endl;
yadayada.remove_if([](int x){ return x > 100; });
etcetera.remove_if([](int x){ return x > 200; });
cout << "Trimmed list:\n";
for_each(yadayada.begin(), yadayada.end(), outint);
cout << endl;
for_each(etcetera.begin(), etcetera.end(), outint);
cout << endl;
return 0;
}
运行结果如下: