C++Primer练习[第六章]

//练习6.1:实参和形参的区别是什么?
//答:形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。实参是形参的初始值。
//形参名是可选的,不命名形参表示在函数体内不会使用它,但是,是否设置未命名的形参并不影响调用时提供的实参数量。即使某个形参不被函数使用,也必须为它提供一个实参。


//练习6.2:请指出下列函数哪个有错误,为什么?应该如何修改这些错误呢?
//(a)int f(){
// string s;
// //…
// return s;
// }
//(b)f2(int i){/*…*/ }
//(c)int calc(int v1, int v1) /*…*/ }
//(d)double square(double x) return x*x;
//答:(a)有错误。return语句返回s的值的类型与函数定义的返回类型不一致。
//应改为:
//string f() {
// string s;
// //…
// return s;
// }
//或者
//int f() {
// int s;
// //…
// return s;
// }
//(b)有错误。函数定义缺少返回类型。一个典型的函数定义包括以下部分:返回类型、函数名字、由0个或多个形参组成的列表以及函数体。
//应改为:
//void f2(int i){/*…*/ }
//(c)有错误,1.任意两个形参都不能同名 2.函数体少了{ 3.没有用return 语句结束函数并返回值。
//应改为:
//int calc(int v1, int v2) {int res;/*…*/ return res;}
//(d)有错误。函数体应是一个语句块,要用{}。
//应改为:
//double square(double x) {return x*x;}


//练习6.3:编写你自己的fact函数,上机检查是否正确。
#include <iostream>
#include <stdexcpt.h>
using std::runtime_error;
using std::cin;
using std::cout;
using std::endl;
int fact(int val)
{
try
{
if (val <= 0)
{
throw runtime_error("输入值要大于0");
}
int ret = 1;
for (int i = 0; i != val; ++i)
{
ret *= i + 1;
}
return ret;
}
catch (runtime_error e)
{
cout << e.what() << endl;
return val;
}
}
int main()
{
int val;
cout << "请输入要计算的阶乘" << endl;
cin >> val;
int ret = fact(val);
if (ret > 0)
{
cout << val << "!是: " << ret << endl;
}
system("pause");
return 0;
}




//练习6.4:编写一个与用户交互的函数,要求用户输入一个数字,计算生成该数字的阶乘。在main函数中调用该函数。
#include <iostream>
#include <stdexcpt.h>
using std::runtime_error;
using std::cin;
using std::cout;
using std::endl;
int fact()
{
int val;
cout << "请输入要计算的阶乘" << endl;
try
{
cin >> val;
if (val <= 0)
{
throw runtime_error("输入值要大于0");
}
int ret = 1;
for (int i = 0; i != val; ++i)
{
ret *= i + 1;
}
cout << val << "!是: " << ret << endl;
return ret;
}
catch (runtime_error e)
{
cout << e.what() << endl;
return val;
}
}
int main()
{
fact();


system("pause");
return 0;
}




//练习6.5:编写一个函数输出实参的绝对值。
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
int fun(int val)
{
if (val < 0)
{
return -1 * val;
}

return val;
}
int main()
{
int val;
cout << "请输入一个整数:" << endl;
cin >> val;
cout << val << "的绝对值是: " << fun(val) << endl;
system("pause");
return 0;
}


//练习6.6:说明形参,局部变量以及局部静态变量的区别。编写一个函数,同时用到这三种形式。
//答:形参是一种自动对象。函数开始时为形参申请存储空间,因为形参定义在函数体作用域之内,所以一旦函数终止,形参也将被销毁。
//形参和函数体内部定义的变量统称为局部变量。他们对函数而言是局部的,仅在函数的作用域内可见。局部变量的声明周期依赖于定义的方式。
//局部静态变量在程序的执行路径第一次经过对象定义语句时初始化,并且直到程序终止才被销毁,在此期间即使对象所在的函数结束执行也不会对它有影响。
#include <iostream>
using std::cout;
using std::endl;
int fun(int val)
{
//val是形参 也是局部变量
//i是局部变量
//s_i是静态变量
int i = 10;
static int s_i = 0;
return val * i + (++s_i);
}
int main()
{
for (int i = 0; i < 10; ++i)
{
cout << fun(i) << endl;
}
system("pause");
return 0;
}


//练习6.7:编写一个函数,当它第一次被调用时返回0,以后每次被调用返回值加1。
#include <iostream>
using std::cout;
using std::endl;
int fun()
{
static int i = 0;
return i++;
}
int main()
{
for (int i = 10; i < 20; ++i)
{
cout << fun() << endl;
}
system("pause");
return 0;
}


//练习6.8:编写一个名为Chapter6.h的头文件,令其包含6.1节练习(第184页)中的函数声明。
//写到Chapter6.h这个文件中。
#ifndef CHAPTER_6_H
#define CHAPTER_6_H
int fact(int val);
int fact();
int fun(int val);
#endif


//练习6.9:编写你自己的fact.cc和factMain.cc, 这两个文件都应该包含上一小节的练习中编写的chapter6.h的头文件。通过这些文件,理解你的编译器是如何支持分离式编译的。
//fact.cpp
#include <iostream>
#include <stdexcpt.h>
#include "chapter6.h"
using std::runtime_error;
using std::cin;
using std::cout;
using std::endl;
int fact(int val)
{
try
{
if (val <= 0)
{
throw runtime_error("输入值要大于0");
}
int ret = 1;
for (int i = 0; i != val; ++i)
{
ret *= i + 1;
}
return ret;
}
catch (runtime_error e)
{
cout << e.what() << endl;
return val;
}
}
int fact()
{
int val;
cout << "请输入要计算的阶乘" << endl;
try
{
cin >> val;
if (val <= 0)
{
throw runtime_error("输入值要大于0");
}
int ret = 1;
for (int i = 0; i != val; ++i)
{
ret *= i + 1;
}
cout << val << "!是: " << ret << endl;
return ret;
}
catch (runtime_error e)
{
cout << e.what() << endl;
return val;
}
}
int fun(int val)
{
if (val < 0)
{
return -1 * val;
}


return val;
}
//factMain.cpp
#include <iostream>
#include "chapter6.h"
using std::cin;
using std::cout;
using std::endl;
int main()
{
//6.3
{
int val;
cout << "请输入要计算的阶乘" << endl;
cin >> val;
int ret = fact(val);
if (ret > 0)
{
cout << val << "!是: " << ret << endl;
}
}


//6.4
{
fact();
}


//6.5
{
int val;
cout << "请输入一个整数:" << endl;
cin >> val;
cout << val << "的绝对值是: " << fun(val) << endl;
}


system("pause");
return 0;
}


//练习6.10:编写一个函数,使用指针形参交换两个整数的值。在代码中调用该函数,并输出交换后的结果,以此验证函数的正确性。
#include <iostream>
using std::cout;
using std::endl;
void fun(int* val1, int* val2)
{
int temp = *val1;
*val1 = *val2;
*val2 = temp;
}
int main()
{
int val1 = 10;
int val2 = 20;
cout << "交换前:" << val1 << " " << val2 << endl;
fun(&val1, &val2);
cout << "交换后:" << val1 << " " << val2 << endl;
system("pause");
return 0;
}


//练习6.11:编写并验证你自己的reset函数,使其作用于引用类型的参数
#include <iostream>
using std::cout;
using std::endl;
void reset(int &val)
{
val = 0;
}
int main()
{
int val = 10;
cout << "reset前:" << val << endl;
reset(val);
cout << "reset后:" << val << endl;
system("pause");
return 0;
}


//练习6.12:改写6.2.1节中练习6.10(第188页)的程序,使用引用而非指针交换两个整数的值。你觉得哪种方法更易于使用呢?为什么?
//答:传引用更易于使用,因为只需要写变量名而不需要取地址符
#include <iostream>
using std::cout;
using std::endl;
void fun(int& val1, int& val2)
{
int temp = val1;
val1 = val2;
val2 = temp;
}
int main()
{
int val1 = 10;
int val2 = 20;
cout << "交换前:" << val1 << " " << val2 << endl;
fun(val1, val2);
cout << "交换后:" << val1 << " " << val2 << endl;
system("pause");
return 0;
}


//练习6.13:假设类型T是某种类型的名字,说明以下两个函数声明的区别:一个是void f(T), 另一个是void f(&T)
//答:void f(T)声明了一个参数为T类型,返回类型为void,函数名为f的函数。在调用的时候,通过传值调用,把实参拷贝给形参。形参发生变化时,实参跟着变化。
//void f(&T)声明了一个参数为T类型引用,返回类型为void,函数名为f的函数。在调用的时候,通过传引用调用,将形参与实参绑定。一旦引用的形参发生变化,实参也会跟着变化


//练习6.14:举一个形参应该是引用类型的例子,再举一个形参不能是引用类型的例子。
//答:如果需要返回多个值应该用引用。
//如:int fun(int &res, int val)
// {
// res = val * val;
// return val + val;
// }
//似乎都可以用引用类型,如果不想影响到实参,可用常量引用。


//练习6.15:说明find_char函数中的三个形参为什么是现在的类型,特别说明为什么s是常量引用而occurs是普通引用?为什么s和occurs是引用类型而c不是?如果令s是普通引用会发生什么情况?如果令occurs是常量引用会发生什么情况?
//答:作为查找范围的对象s是个string对象,可能会非常长,所以应该尽量避免直接拷贝,所以应该用引用传递。又因为无须改变引用参数的值,所以我们将其声明为常量引用。
//作为要找的字符c是个char对象,只占1个字节,所以可以直接用拷贝的。
//作为用于保存字符出现次数的occurs是个size_type对象,它需要改变实参,令其保存字符出现的次数,所以用普通引用。
//如果令s是普通引用则需要注意不要改变到s的值。
//如果令occurs是常量引用则无法改变occurs的值导致无法实现返回多个值的效果,不能实现统计次数的功能。


//练习6.16:下面的这个函数虽然合法,但是不算特别有用。指出它的局限性并设法改善。
//bool is_empty(string &s) { return s.empty(); }
//答:局限性在于这么做给函数调用者一种误导,即函数可以修改它的实参的值。此外,使用引用而非常量引用也会极大的限制函数所能接受的实参类型。在这个例子中,我们不能把const对象、字面值或者需要类型转换的对象传递给普通的引用对象。
//可改为:
//bool is_empty(const string &s) { return s.empty(); }


//练习6.17:编写一个函数,判断string对象中是否含有大写字母。编写另一个函数,把string对象全都改成小写形式。在两个函数中你使用的类型相同吗?为什么?
//答:不相同,因为第二个函数不需要改变原string对象,所以用const string&类型的形参,以免受到太多局限性。第二个函数需要改变原string对象,所以用string&类型的形参。
#include <iostream>
#include <cstring>
#include <string>
using std::string;
using std::cout;
using std::endl;
bool IsHaveUpper(const string &_str)
{
for (auto c : _str)
{
if (isupper(c))
{
return true;
}
}


return false;
}
void ChangeStr(string &_str)
{
for (auto &c : _str)
{
if (isupper(c))
{
c = tolower(c);
}
}
}
int main()
{
string str = "Hello World";
//判断string对象中是否含有大写字母
if (IsHaveUpper(str))
{
cout << str << " 有大写字母" << endl;
}
else
{
cout << str << " 没有大写字母" << endl;
}
//把string对象全都改成小写形式
cout << "转换前的字符串为:" << str << endl;
ChangeStr(str);
cout << "转换后的字符串为:" << str << endl;
system("pause");
return 0;
}


//练习6.18:为下面的函数编写函数声明,从给定的名字中推测函数具备的功能。
//(a)名为compare的函数,返回bool值,两个参数都是matrix类的引用。
//(b)名为change_val的函数,返回vector的迭代器,有两个参数:一个是int,另一个是vector的迭代器。
//答:(a)函数声明:bool compare(const matrix &val1, const matrix &val2);
//推测功能:对matrix类的两个对象进行对比返回对比结果。
//(b)函数声明:vector<int>::iterator change_val(int val1, vector<int>::iterator val2);
//推测功能:改变迭代器val2的值为val1并返回改变后的迭代器。


//练习6.19:假定有如下声明,判断哪个调用合法,哪个调用不合法。对于不合法的函数调用,说明原因。
//double calc(double);
//int count(const string&, char);
//int sum(vector<int>::iterator, vector<int>::iterator, int);
//vector<int> vec(10);
//(a)calc(23.4, 55.1); (b)count("abcda", 'a'); 
//(c)calc(66); (d)sum(vec.begin(), vec.end(), 3.8);
//答:(a)不合法。参数过过。
//(b)合法。
//(c)合法。
//(d)合法。但是会丢失精度。


//练习6.20:引用形参什么时候应该是常量引用?如果形参应该是常量引用,而我们将其设为了普通引用,会发生什么情况?
//答:当我们不需要对引用进行修改的时候应该用常量引用。
//如果形参应该是常量引用,而我们将其设为了普通引用,会极大的限制函数所能接受的实参类型。且会给函数调用者一种函数可以修改它的实参的值的误导。


//练习6.21:编写一个函数,令其接受两个参数:一个是int型的数,另一个是int指针。函数比较int的值和指针所指的值,返回较大的那个。在该函数中指针的类型应该是什么? 
//答:该函数中指针的类型应该是const int *
#include <iostream>
using std::cout;
using std::endl;
int bijiao(const int val, const int * val_p)
{
return (val > *val_p) ? val : *val_p;
}
int main()
{
int a = 10;
int b = 20;
int *p = &b;
cout << "比较大的数是:" << bijiao(a, p) << endl;
system("pause");
return 0;
}


//练习6.22:编写一个函数,令其交换两个int指针。
#include <iostream>
using std::cout;
using std::endl;
void jiaohuan(int * (&val_p_1), int * (&val_p_2))
{
int * temp = val_p_1;
val_p_1 = val_p_2;
val_p_2 = temp;
}
int main()
{
int a = 10;
int b = 20;
int *p1 = &a;
int *p2 = &b;
cout << "交换前:p1 " << p1 << " p2 " << p2 << endl;
jiaohuan(p1, p2);
cout << "交换后:p1 " << p1 << " p2 " << p2 << endl;
system("pause");
return 0;
}


//练习6.23:参考本节介绍的几个print函数,根据理解编写你自己的版本。依次调用每个函数使其输入下面定义的i和j: 
//int i = 0, j[2] = { 0,1 };
#include <iostream>
using std::cout;
using std::endl;
using std::begin;
using std::end;
void print(const int* val)
{
cout << "void print(const int* val)" << endl;
cout << *val << endl;
}
void print(const int *beg, const int *end)
{
cout << "void print(const int *beg, const int *end)" << endl;
for(;beg!=end;++beg)
cout << *beg << endl;
}
void print(const int ia[], size_t size)
{
cout << "void print(const int ia[], size_t size)" << endl;
for (size_t i = 0; i != size; ++i)
cout << *(ia+i) << endl;
}
int main()
{
int i = 0, j[2] = { 0,1 };
//void print(const int* val)
print(&i);
print(&j[0]);
print(&j[1]);
//void print(const int *beg, const int *end)
print(&i, &i + 1);
print(begin(j), end(j));
//void print(const int ia[], size_t size)
print(&i, 1);
print(j, end(j) - begin(j));


system("pause");
return 0;
}


//练习6.24:描述下面这个函数的行为。如果代码中存在问题,请指出并改正。
//void print(const int ia[10])
//{
// for (size_t i = 0; i != 10; ++i)
// cout << ia[i] << endl;
//}
//答:循环输出含有十个int类型数组的值。
//问题在于数组长度被固定了,可用使用标准库规范,或者显式传递一个表示数组大小的形参来改进。
//使用标准库规范
//void print(const int *beg, const int *end)
//{
// while (beg != end)
// cout << *beg++ << endl;
//}
//显式传递一个表示数组大小的形参
//void print(const int ia[], size_t size)
//{
// for(size_t i = 0; i !+ size; ++i)
// {
// cout << ia[i] << endl;
// }
//}


//练习6.25:编写一个mian函数,令其接受两个实参,把实参的内容连接成一个string对象并输出出来。
#include <iostream>
using std::cout;
using std::endl;
int main(int argc, char *argv[])
{
for (size_t i = 1; i < argc; ++i)
{
cout << argv[i];
}
cout << endl;
system("pause");
return 0;
}


//练习6.26:编写一个程序,使其接受本节所示的选项;输出传递给main函数的实参的内容。
#include <iostream>
using std::cout;
using std::endl;
int main(int argc, char *argv[])
{
for (size_t i = 0; i < argc; ++i)
{
cout << "argv["<< i << "] = \""<< argv[i]<<"\"" << endl;
}
cout << "argv[" << argc << "] = \"" << 0 << "\"" << endl;
system("pause");
return 0;
}


//练习6.27:编写一个函数,它的参数是initializer_list<int>类型的对象,函数的功能是计算列表中所有元素的和。
#include <iostream>
#include <initializer_list>
using std::cout;
using std::endl;
using std::initializer_list;
int sum(initializer_list<int> list)
{
int sum = 0;
for (const auto &val : list)
{
sum += val;
}
return sum;
}
int main()
{
cout << sum({ 1,2,3,4,5,6,7,8,9,10 }) << endl;
system("pause");
return 0;
}


//练习6.28:在error_msg函数的第二个版本中包含ErrCode类型的参数,其中循环内的elem是什么类型。 
//答:const string


//练习6.29:在范围for循环使用initializer_list对象时,应该将循环控制变量声明成引用类型吗,为什么?
//答:看情况,如果是内置类型不应该,因为initializer_list对象中的元素永远是常量值,我们无法改变initializer_list对象中元素的值,用引用无意义。
//如果是类或者字符串之类有可能很大或者可能有初始化问题的时候,应该,可以节省复制开销。


///
//省略符形参使用例子
#include <iostream>
#include <cstdarg>
using std::cout;
using std::endl;


int sum(int a, ...)
{
va_list list;
va_start(list, a);
int sum = 0;
for (int val = a; val != -1; val = va_arg(list, int))
{
sum += val;
}
va_end(list);
return sum;
}
int main()
{
cout << sum(1,2,3,4,5,6,7,8,9,10,-1) << endl;
system("pause");
return 0;
}
///


//练习6.30:编译第200页的str_subrange函数,看看你的编译器是如何处理函数中的错误的
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
bool str_subrange(const string &str1, const string &str2)
{
if (str1.size() == str2.size())
return str1 == str2;
auto size = (str1.size() < str2.size()) ? str1.size() : str2.size();
for (decltype(size) i = 0; i != size; ++i)
{
if (str1[i] != str2[i])
return;
}
}
int main()
{
string str1("hello");
string str2("world");
str_subrange(str1, str2);
system("pause");
return 0;
}


//练习6.31:什么情况下返回的引用无效?什么情况下返回常量的引用无效? 
//答:返回值是局部变量时,返回的常量引用无效。


//练习6.32:下面的函数合法吗?如果合法,说明其功能;如果不合法,修改其中的错误并解释原因。
//int &get(int *arry, int index){ return arry[index]; }
//int main()
//{
// int ia[10];
// for (int i = 0; i != 10; ++i)
// get(ia, i) = i;
//}
//答:不合法。arry是局部变量。
//修改为:
//int &get(int *(&arry), int index){ return arry[index]; }
//int main(){
// int ia[10];
// for (int i = 0; i != 10; ++i)
// get(ia, i) = i;
//}


//练习6.33:编写一个递归函数,输出vector对象的内容。
#include <iostream>
#include <vector>
#include <string>
using std::string;
using std::vector;
using std::cout;
using std::endl;
void prvector(vector<string>::iterator it, vector<string>::iterator end)
{
if (it != end)
{
cout << (*it) << endl;
prvector(++it, end);
}
return;
}
int main()
{
vector<string> str{ "hello", "world", "ni", "hao", "ma" };
prvector(str.begin(), str.end());
system("pause");
return 0;
}


//练习6.34:如果factorial函数的停止条件如下所示,将发生什么情况?
//if(val != 0)
//答:多执行一次,结果没影响。


//练习6.35:在调用factorial函数时,为什么我们传入的值是val – 1而非 val-- ?
//答:因为我们在*中有用到val的值,如果用val--会改变到val,而*的求值算顺序是不定的,会有问题。


//练习6.36:编写一个函数的声明,使其返回数组的引用并且该函数包含10个string对象。不要使用尾置返回类型、decltype或者类型别名。
#include <iostream>
#include <string>
using std::string;
string (&fun(string (&slist)[10]))[10]
{
return slist;
}
int main()
{
string slist[10]{ "1","2","3","4","5","6","7","8","9","10" };
for (auto str : fun(slist))
{
std::cout << str << std::endl;
}
system("pause");
return 0;
}


//练习6.37:为上一题的函数再写三个声明,一个使用类型别名,另一个使用尾置返回类型、最后一个使用decltype关键字。你觉得哪种形式最好,为什么? 
//答:用类型别名最好,能清晰的知道返回类型是什么,且可以取个名字重复使用。
#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::endl;
string slist[10]{ "1","2","3","4","5","6","7","8","9","10" };
string (&fun0())[10]
{
return slist;
}
//类型别名
using funtype = string(&)[10];
funtype fun1()
{
return slist;
}
//尾置返回类型
auto fun2()->string(&)[10]
{
return slist;
}
//decltype关键字
decltype(slist) &fun3()
{
return slist;
}
int main()
{
cout << "普通方法" << endl;
for (auto str : fun0())
{
std::cout << str << " ";
}
cout << endl;
cout << "类型别名" << endl;
for (auto str : fun1())
{
std::cout << str << " ";
}
cout << endl;
cout << "尾置返回类型" << endl;
for (auto str : fun2())
{
std::cout << str << " ";
}
cout << endl;
cout << "decltype关键字" << endl;
for (auto str : fun3())
{
std::cout << str << " ";
}
cout << endl;
system("pause");
return 0;
}


//练习6.38:修改arrPtr函数,使其返回数组的引用。 
//答:改为
//int odd[] = { 1,3,5,7,9 };
//int even[] = { 0,2,4,6,8 };
//decltype(odd) (&arrPtr)(int i)
//{
// return (i % 2) ? odd : even;
//}


//练习6.39:说明在下面的每组声明中第二条声明语句是何含义。如果有非法的声明,请指出来。
//(a)int calc(int, int);
// int calc(const int, const int);
//(b)int get();
// double get();
//(c)int *reset(int *);
// double *reset(double *);
//答:(a)声明函数calc,返回值类型是int,有两个参数,参数类型都是const int. 这个声明是非法的,因为顶层const不影响传入函数的对象,一个拥有顶层const的形参无法和另一个没有顶层const的形参区分开来。等于重复声明。
//(b)声明函数get,返回值类型是double,没有参数。这个声明是非法的。因为不允许两个函数除了返回类型以外其他所有的要素都相同。
//(c)声明函数reset,返回值类型是double *,有一个参数,参数类型是double *.这个声明是合法的。


//练习6.40:下面的哪个声明是错误的?为什么?
//(a)int ff(int a, int b = 0, int c = 0);
//(b)char *init(int ht = 24, int wd, char bckgrnd);
//答:(a)正确。
//(b)错误。因为一旦某个形参被赋予了默认值,它后面的所有形参都必须有默认值。


//练习6.41:下面的哪个调用是非法的?为什么?哪个调用虽然合法但显然与程序员的初衷不符?为什么?
//char *init(int ht, int wd = 80, char bckgrnd = ' ');
//(a)init(); (b)init(24, 10); (c)init(14, '*');
//答:(a)非法。因为函数声明的第一个参数是没有默认参数的,必须给值
//(b)合法
//(c)合法但是初衷不符。因为该调用之所以合法是因为发生么隐式转换,'*'被转换成了整数类型,这个值并不是想要的wd值。


//练习6.42:给make_plural函数(参见6.3.2节,第201页)的第二个形参赋予默认实参's', 利用新版本的函数输出单词success和failure的单数和复数形式。
#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::endl;
string make_plural(size_t ctr, const string &word, const string &ending = "s")
{
return (ctr > 1) ? word + ending : word;
}
int main()
{
cout << make_plural(1, "success") << endl;
cout << make_plural(2, "success", "es") << endl;
cout << make_plural(1, "failure") << endl;
cout << make_plural(2, "failure") << endl;
system("pause");
return 0;
}


//练习6.43:你会把下面的哪个声明和定义放在头文件中?哪个放在源文件中?为什么?
//(a)inline bool eq(const, BigInt &, const BigInt &){...}
//(b)void putValues(int *arr, int size);
//答:(a)声明和定义都放在头文件中。因为这是个内联函数。内联函数可以在程序中多次定义,不过,对于某个给定的内联函数来说,他的多个定义必须完全一直。基于这个原因,内联函数通常定义在头文件中。
//(b)声明放在头文件中,定义放在源文件中。避免重复定义。


//练习6.44:将6.2.2节(第189页)的isShorter函数改写成内联函数。
//答:
//inline bool isShorter(const string &s1, const string &s2)
//{
// return s1.size() < s2.size();
//}


//练习6.45:回顾在前面的练习中你编写的那些函数,它们应该是内联函数吗 ? 如果是,将它们改写成内联函数:如果不是,说明原因。
//答:如果是规模较小、流程直接、频繁调用的函数,应该是内联函数。可以在函数的返回类型前面加上关键字inline,就可以将它改写成内联函数了。
//如果不是以上原因,就不应该是内联函数。


//练习6.46:能把isShorter函数定义成constexpr函数吗?如果能,将它改写成constexpr函数,如果不能,说明原因。
//答:不能。定义constexpr函数需要遵循几项约定:函数的返回类型及所有形参的类型都得是字面值类型,而且函数体中必须有且只有一条return语句。
//isShorter函数的返回值不会生成常数表达式所以不能。


//练习6.47:改写6.3.2节(第205页)练习中使用递归输出vector内容的程序,使其有条件地输出与执行过程有关的信息。例如,每次调用时输出vector对象的大小。分别在打开和关闭调试器的情况下编译并执行这个程序。
//打开调试器
#include <iostream>
#include <vector>
#include <string>
using std::string;
using std::vector;
using std::cout;
using std::endl;
void prvector(vector<string>::iterator it, vector<string>::iterator end)
{
if (it != end)
{
cout << (*it) << endl;
prvector(++it, end);
}
return;
}
int main()
{
vector<string> str{ "hello", "world", "ni", "hao", "ma" };
#ifndef NDEBUG
cout << "vector对象的大小" << str.size() << endl;
#endif


prvector(str.begin(), str.end());
system("pause");
return 0;

}


//关闭调试器
#include <iostream>
#include <vector>
#include <string>
#define NDEBUG
using std::string;
using std::vector;
using std::cout;
using std::endl;
void prvector(vector<string>::iterator it, vector<string>::iterator end)
{
if (it != end)
{
cout << (*it) << endl;
prvector(++it, end);
}
return;
}
int main()
{
vector<string> str{ "hello", "world", "ni", "hao", "ma" };
#ifndef NDEBUG
cout << "vector对象的大小" << str.size() << endl;
#endif


prvector(str.begin(), str.end());
system("pause");
return 0;
}


//练习6.48:说明下面这个循环的含义,它对assert的使用合理吗?
//string s;
//while (cin >> s && s != sought) {} //空函数体
//assert(cin);
//答:循环输入s当cin还没遇到结束符或者输入的数值不等于sought的时候执行函数体,否则退出函数体
//不合理。如果是cin遇到结束符,则assert会输出信息并终止程序的执行。


//练习6.49:什么是候选函数?什么是可执行函数? 
//答:调用对应的重载函数集中的函数称为候选函数。候选函数具备两个特征:一是与被调用的函数同名,二是其声明在调用点可见。
//从候选函数中选出的能被这组实参调用的函数称为可行函数。可行函数具备两个特征:一是其形参数量与本次调用提供的实参数量相等,二是每个实参的类型与对应的形参类型相同,或者能转换成形参的类型。


//练习6.50:已知有第217页对函数f的声明,对于下面的每一个调用列出可行函数。其中哪个函数是最佳匹配?如果调用不合法,是因为没有可匹配的函数还是因为调用具有二义性?
//(a)f(2.56, 42) (b)f(42) (c)f(42, 0) (d)f(2.56, 3.14)
//答:(a)调用不合法。调用具有二义性
//(b)合法。调用void f(int);
//(c)合法。调用void f(int, int);
//(d)合法。调用void f(double,double=3.14);


//练习6.51:编写函数f的4个版本,令其输出一条可以区分的信息。验证上一个练习的答案,如果你回答错了,反复研究本节的内容知道你弄清楚自己错在何处
#include <iostream>
using std::cout;
using std::endl;
void f()
{
cout << "调用" << __func__ << "()" << endl;
}
void f(int)
{
cout << "调用" << __func__ << "(int)" << endl;
}
void f(int, int)
{
cout << "调用" << __func__ << "(int, int)" << endl;
}
void f(double, double = 3.14)
{
cout << "调用" << __func__ << "(double, double = 3.14)" << endl;
}
int main()
{
//f(2.56, 42);
f(42);
f(42, 0);
f(2.56, 3.14);
system("pause");
return 0;
}


//练习6.52 已知有如下声明,
// void manip(int, int);
// double dobj;
//请指出下列调用中每个类型转换的等级(参见6.6.1节,第219页)
// (a) manip('a', 'z'); (b)manip(55.4, dobj);
//答:(a)3类型提升(b)3类型提升


//练习6.53 说明下列每组声明中的第二条语句会产生什么影响,并指出哪些不合法(如果有的话)
//(a)int calc(int&, int&);
// int calc(const int&, const int&);
//(b)int calc(char*, char*);
// int calc(const char*, const char*);
//(c)int calc(char*, char*);
// int calc(char* const, char* const);
//答:(a)重载函数,参数是常量引用。
//(b)重载函数,参数是指向常量的指针。
//(c)不合法。参数是常量指针,会出现重定义,因为如果传一个指针实参则无法区分是要调用哪一个。


//练习6.54 编写函数的声明,令其接收两个int形参并且返回类型也是int;然后声明一个vector对象,令其元素是指向该函数的指针。
//答:
//int fun(int, int);
//vector<decltype(fun)*> vec;


//练习6.55 编写4个函数,分别对两个int值执行加、减、乘、除运算;在上一题创建的vector对象中保存这些函数的指针。
#include <iostream>
#include <vector>
using namespace std;
int fun1(int a, int b)
{
return a + b;
}
int fun2(int a, int b)
{
return a - b;
}
int fun3(int a, int b)
{
return a * b;
}
int fun4(int a, int b)
{
return a / b;
}


int main()
{
vector<decltype(fun1)*> vec{fun1, fun2, fun3, fun4};
system("pause");
return 0;
}


//练习6.56 调用上述vector对象的每个元素并输出其结果
#include <iostream>
#include <vector>
using namespace std;
int fun1(int a, int b)
{
return a + b;
}
int fun2(int a, int b)
{
return a - b;
}
int fun3(int a, int b)
{
return a * b;
}
int fun4(int a, int b)
{
return a / b;
}
int main()
{
vector<decltype(fun1)*> vec{fun1, fun2, fun3, fun4};
for (auto fun : vec)
{
cout << fun(10, 5) << endl;
}


system("pause");
return 0;

}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值