習(xí)題選自:C++ Primer Plus(第六版)
內(nèi)容僅供參考,如有錯誤,歡迎指正 !
復(fù)習(xí)題
1. 使用用大括號括起的初始化列表語法重寫下述代碼。重寫后的代碼不應(yīng)使用數(shù)組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. 在下述簡短的程序中,哪些函數(shù)調(diào)用不對?為什么?對于合法的函數(shù)調(diào)用,指出其引用參數(shù)指向的是什么。
#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左值引用參數(shù)的時候,參數(shù)將被初始化為左值。將右值傳遞給函數(shù)時,const左值引用參數(shù)將指向右值的臨時拷貝。將左值傳遞給非const左值引用參數(shù)時,參數(shù)將被初始化為左值;但非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);調(diào)用void r1(double &rx)。另外兩個實參均為右值,const左值引用可以指向他們的拷貝?!緦⒂抑祩鬟f給函數(shù)時,const左值引用參數(shù)將指向右值的臨時拷貝?!?。
b.
double & rx
double && rx
double && rx
左值引用與左值實參w匹配,而右值引用與兩個右值實參匹配。
c.
const double & rx
double && rx
double && rx
const左值引用與左值實參w匹配,而右值引用與兩個右值實參匹配。
總之,非const左值形參與左值實參匹配,非cosnt右值形參與右值實參匹配;const左值形參可以與左值或右值實參匹配。如果可供選擇的話,編譯器優(yōu)先選擇前兩種方式。
4. 哪些成員函數(shù)是特殊的成員函數(shù)?它們特殊的原因是什么?
特殊成員函數(shù):默認構(gòu)造函數(shù)、復(fù)制構(gòu)造函數(shù)、移動構(gòu)造函數(shù)、析構(gòu)函數(shù)、復(fù)制賦值運算符和移動賦值運算符。這些函數(shù)之所以特殊,是因為編譯器將根據(jù)情況自動提供它們的默認版本。
5. 假設(shè)Fizzle類只有如下所示的數(shù)據(jù)成員:
class Fizzle
{
private:
double bubbles[4000];
...
};
為什么不適合給這個類定義移動構(gòu)造函數(shù)?要讓這個類適合定義移動構(gòu)造函數(shù),應(yīng)如何修改存儲4000個double值的方式?
移動構(gòu)造函數(shù)是在轉(zhuǎn)讓數(shù)據(jù)所有權(quán)可行的時候是合適的。但對于標準數(shù)組沒有轉(zhuǎn)讓所有權(quán)的機制,因此不適合給該類定義移動構(gòu)造函數(shù)。如果Fizzle使用指針和動態(tài)內(nèi)存分配來存儲這4000個double值,即可以將數(shù)據(jù)的地址賦給新指針,以轉(zhuǎn)讓其所有權(quán),則適合給Fizzle定義移動構(gòu)造函數(shù)。
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表達式而不是函數(shù)符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);
}
}
編程練習(xí)
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;
}
請?zhí)峁┖瘮?shù)average_list( ),讓該程序變得完整。它應(yīng)該是一個模板函數(shù),其中的類型參數(shù)指定了用作函數(shù)參數(shù)的initilize_list模板的類型以及函數(shù)的返回類型。
修改后:
#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;
};
函數(shù)operator+ ( )應(yīng)創(chuàng)建一個對象,其成員qcode和zcode有操作數(shù)的相應(yīng)成員拼接而成。請?zhí)峁橐苿訕?gòu)造函數(shù)和移動賦值運算符實現(xiàn)移動語義的代碼。編寫一個使用所有這些方法的程序。為方便測試,讓各個方法都顯示特定的內(nèi)容,以便知道它們被調(diào)用。
代碼如下:
#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. 編寫并測試可變參數(shù)模板函數(shù)sum_value( ),它接受任意長度的參數(shù)列表(其中包含數(shù)值,但可以是任何類型),并以long double的方式返回這些數(shù)值的和。
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替換函數(shù)outint( ),并將函數(shù)符替換為兩個匿名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;
}