C++ primer 5th 第6章 函数
=====================================================================
第6章 函数 182页 函数基础
=====================================================================
//QQ108201645编写
#include <iostream>
using namespace std;
//val的阶乘是val*(val-1)*(val-2)...*((val-(val-1))*1)
int fact(int val)
{
//编写函数1*2*3*4*5=120
int ret = 1;//局部变量,用于保存计算结果
while (val > 1)
ret *= val--;//把ret和val的乘积赋给ret,然后将val减1
return ret;//返回结果
}
/*
函数调用代码等价于
int val=5;//字面值初始为5
int ret=1;//用于函数体内的代码
while(val>1)
ret*=val--;
return ret;//用ret的副本初始化j
*/
int main()
{
int j = fact(5);//j等于120.即fact(5)的结果
cout << "5! is" << j << endl;//输出120
return 0;
}
=====================================================================
第6章 函数 183页 函数形参列表
=====================================================================
//QQ108201645编写
#include <iostream>
using namespace std;
int fact(int val)
{
int ret = 1;
while (val > 1)
{
ret *= val--;
}
return ret;
}
void f1(){/*...*/}//隐式的定义空形参列表
void f2(void) {/*...*/ }//显示的定义空形参列表
#ifdef DECLARATION
int f3(int v1,v2) {/*...*/ }//错误int函数要返回一个int类形,v2形参没有类型
int f4(int v1, int v2) {/*...*/ }//要返回一个int 类型
#endif
int temp1() { return 0; }//正确,返回一个int参数
char temp2(int v1, int v2) { return 'a'; }//正确,返回一个char参数
//练习6.3:编写你自己的fact函数,上机检查是否正确。
void fact()
{
cout << "my fact function\n";
}
//练习6.4:编写一个与用户交互的函数,如下
int factorial()
{
cout << "intput a number:";
int i = 0, sum = 1;
cin >> i;
while (i > 0)
{
sum *= i--;
}
return sum;
}
int main()
{
#ifdef DECLARATION
//上面的函数只有一个int类型,所以每次调用它,必须提供一个能转换成int的实参
fact("hello");//错误:实参类型不正确
fact();//错误:实参数量不足
fact(42, 10, 0);//错误:实参数量过多
fact(3.14);//正确,该实参能转换int类型
//练习6.1:实参和形参的区别是什么?
//实参是形参的初始值, 实参用于初始化形参
//练习6.2:请指出下列函数哪个有错误,为什么?应该如何修改这些错误呢?
//(a)
int f()
{
string s;
// ...
return s;
}
//返回值和返回类型不一致, 应该将返回类型改为string,或将s改成return 0;或其它int类型
(b)
f2(int i)
{
/* ... */
}
//缺少返回值, 应该再函数名前加上void
(c) int calc(int v1, int v1)
{
/* ... */
}
//函数体错误, 函数体是一对花括号括起来的块
(d) double square(double x) return x * x;
//函数体错误, 函数体是一对花括号括起来的块
#endif
//练习6.3:编写你自己的fact函数,上机检查是否正确。
fact();
//练习6.4:编写一个与用户交互的函数,要求用户输入一个数字,计算生成该数字的阶
//乘。在main函数中调用该函数。
cout << factorial() << endl;
system("pause");
return 0;
}
=====================================================================
第6章 函数 185页 局部静态对象
=====================================================================
//QQ108201645编写
#include <iostream>
#include <cstddef>
using namespace std;
size_t count_calls()
{//局部静态对象(local static object)统计函数它自己被调用了多少次
static size_t ctr = 0;
return ++ctr;
}
//练习6.6:说明形参、局部变量以及局部静态变量的区别。编写一个函数,同时用到这三种形式。
//形参是函数的一部分, 是自动创建的, 属于局部变量.局部变量包括形参和函数体内定义的变量.局部静态变量则是定义在函数体内, 但是一直到程序结束时才被销毁
void fun(int i)
{
int sum = 1;
sum += i;
static int n = 10;
sum *= n;
}
//练习6.7:编写一个函数,当它第一次被调用时返回0,以后每次被调用返回值加1。
int fun()
{
static int n = 0;
return n++;
}
int main()
{
for (size_t i=0;i!=10;++i)
cout<<count_calls()<<endl;
fun(7);
fun();
system("pause");
return 0;
}
=====================================================================
第6章 函数 188页 指针形参
=====================================================================
//QQ108201645编写
#include <iostream>
#include <cstddef>
using namespace std;
void reset(int *ip)
{
*ip = 0;
ip = 0;
}
//练习题6.10编写交换函数
void swap(int*a, int *b)
{
int t = *a;
*a = *b;
*b = t;
}
int main()
{
int n = 0, i = 42;
int *p = &n, *q = &i;//p指向n;q指向i
*p = 42;//通过*p改变n的值,p指针地址不变
p = q;//p现在指向了i但是i和n的值都不变
reset(&i);//改变i的值而非i的地址
cout<<" i = "<<i<<endl;//输出i等于0
int a = 1, b = 2;
swap(&a, &b);
cout<<"a= "<<a<<"\t b = "<<b<<endl;
system("pause");
return 0;
}
=====================================================================
第6章 函数 189页 使用引用避免拷贝
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
void reset(int &i)//i是传给reset函数的对象的另一个名字
{
i = 0;
}
bool isShorter(const string& s1, const string& s2)
{//引用形参,又无须改变的可以定义为常量引用
return s1.size() < s2.size();
}
//返回s中c第一次出现的位置索引
//引用形参occurs负责统计c的总次数
string::size_type find_char(const string& s, char c, string::size_type &occurs)
{
auto ret = s.size();//第一次出现的位置
occurs = 0;//设置出现次数形参的值
for (decltype(ret) i = 0; i != s.size(); ++i)
{
if (s[i] == c)
{
if (ret == s.size())//当相同时表示从未给ret赋过值,如果赋过值则表示不是第一次
ret = i;//记录c第一次出现的位置
++occurs;//将出现的次数++
}
}
return ret;//出现次数通过occurs隐式的返回
}
int main()
{
int n = 0, i = 42;
int &r= n;//r绑定了n ,即r是n的另一个名字
r = 42;//把42赋给r,现在n的值是42
r = i;//把i的值 赋给n 现在n的值与i相同
i = r;//把r的值 赋给i,i的值与n相同
int j = 42;
reset(j);
cout<<"j = "<<j<<endl;
//避免string对象直接拷贝它们,用引用形参是比较明智的选择,比较参数不用改变对象长度
cout<<isShorter("abc","abc")<<endl;
//三个参数,作为查找范围的一个string对象,要找的字符以及一个用于保存出现次数的size_type参见3.2.2节
//第79页.对象,假设s是一个string对象,ctr是一个size_type对象,则我们通过如下形式调用find_char函数
size_t ctr = 0;
string s = "bobo";
auto index = find_char(s, 'o', ctr);
cout << index << " " << ctr << endl;
system("pause");
return 0;
}
=====================================================================
#include <iostream>
#include <vector>
#include <cstddef>
using namespace std;
vector<int>::const_iterator find_val(
vector<int>::const_iterator beg,//起始点
vector<int>::const_iterator end,//结束点
int value,//查找的值
vector<int>::size_type &occurs)//返回的值
{
auto res_iter = end;//默认等于向量的结束点
occurs = 0;//查找到的次数初始为0
for(;beg!=end;++beg)
if (*beg == value)//当查找到时
{
if (res_iter == end)//当不等于end时表示被赋过值就不是第一次找到相同的
res_iter = beg;
++occurs;//找到后次数增加
}
return res_iter;//返回一个vector<int>::const_iterator 类型
}
int main()
{
vector<int> ivec;
size_t ctr = 0;
int i;
while (cin>>i)
{
ivec.push_back(i);
}
int array[] = { 1,2,3 };
for (auto i : array)
{
auto it = find_val(ivec.begin(), ivec.end(), i, ctr);//调用函数
//cout<<typeid(it).name()<<endl;//测试it类型
if (it==ivec.end())//当查找vector向量里到end位置时表示没有查到
cout<<i<<" 没有找到相同的数据"<<endl;
else
{
cout<<i<<" 首次出现在位置:"<<it-ivec.begin()<<"\t 出现次数:"<<ctr<<endl;
}
}
system("pause");
return 0;
}
=====================================================================
//等价于下面的代码,已展开
#include <iostream>
#include <vector>
using namespace std;
vector<int>::const_iterator find_val(
vector<int>::const_iterator beg,//起始点
vector<int>::const_iterator end,//结束点
int value,//查找的值
vector<int>::size_type &occurs//返回的值
)
{
vector<int>::const_iterator res_iter = end;//默认等于向量的结束点
occurs = 0;//查找到的次数初始为0
for (; beg != end; ++beg)
{
if (*beg == value)//当查找到时
{
if (res_iter == end)//当不等于end时表示被赋过值就不是第一次找到相同的
res_iter = beg;
++occurs;//找到后次数增加
}
}
return res_iter;//返回一个vector<int>::const_iterator 类型
}
int main()
{
size_t ctr = 0;
int num = 0;
vector<int> ivec;
while (cin>> num)
{
ivec.push_back(num);
}
int arr[] = { 1,2,3 };
for (int i = 0; i < sizeof(arr) / sizeof(int); ++i)
{
vector<int>::const_iterator it = find_val(ivec.begin(), ivec.end(), arr[i], ctr);
//cout<<typeid(it).name()<<endl;//测试it类型
if(it==ivec.end())//当查找vector向量里到end位置时表示没有查到
cout<<arr[i]<<" 找不到数据"<<endl;
else
{
cout << arr[i] << " 首次出现在位置:" << it - ivec.begin() << "\t 出现次数:" << ctr << endl;
}
}
system("pause");
return 0;
}
//QQ108201645编写
=====================================================================
//数组方式
#include <iostream>
using namespace std;
int main()
{
int arr2[] = { 4,5,7,1,2,3 };
int arr[] = { 1,2,3,4,5 };//查找arr数组在arr2中的位置
for (int i = 0,j; i < sizeof(arr) / sizeof(int); ++i)
{
int occurs = 0;
int len = sizeof(arr2) / sizeof(int);
size_t res_iter = len;
for (j = 0; j < len; ++j)
{
if (arr[i] == arr2[j])
{
if (res_iter == len)
res_iter = j;
occurs++;
}
}
if (res_iter==len)//如果找到末尾表示没有找到
cout<<arr[i]<<"没找到数据"<<endl;
else
{
cout << arr[i] << "在位置" << res_iter+1 << endl;
}
}
system("pause");
return 0;
}
=====================================================================
//追加内容
#include <iostream>
#include <vector>
#include <string>
using namespace std;
string::size_type find_char(const string& s, char c, string::size_type &occurs)
{
size_t res = s.size();
occurs = 0;
for (decltype(s.size()) i = 0; i != s.size(); ++i)
{
if (s[i] == c)
{
if (res == s.size())
res = i;
++occurs;
}
}
return res;
}
string::size_type find_char(const string& s, string& c, string::size_type &occurs)
{
size_t res = s.size();
occurs = 0;
for (decltype(s.size()) i = 0; i != s.size(); ++i)
{
auto t = i;
size_t j = 0;
while (j != c.size() && c[j] == s[t])
{
j++;
t++;
}
if (j==c.size())
{
if (res == s.size())
res = i;
++occurs;
}
}
return res;
}
vector<int>::const_iterator find_int(
vector<int>::const_iterator beg,
vector<int>::const_iterator end,
int val,
vector<int>::size_type &occurs
)
{
auto res_iter = end;
occurs = 0;
for (;beg!=end;++beg)
if (*beg == val)
{
if (res_iter == end)
res_iter = beg;
++occurs;
}
return res_iter;
}
int main()
{//单个字符串的字符搜索
string buf;
size_t ctr=0;
find_char("Hello", 'a', ctr);
//循环搜索
cout<<"输入几组字符串,以end结束"<<endl;
vector<string> svec;
while (cin>>buf&&buf!="end")
{
svec.push_back(buf);
}
string ch;
cout<<"输入字符串进行查找"<<endl;
cin >> ch;
cout<<"字符串循环查找"<<endl;
for (auto &s : svec)//搜索在每个向量中的字符串
{
auto it = find_char(s, ch, ctr);
if (it == s.size())
cout << ch << "在:" << s << " 中没找到" << endl;
else
{
cout << ch << "在:" << s << " 中首次在位置:" << it << " 出现:" << ctr << endl;
}
}
cout<<"单个循环查找"<<endl;
for (auto c : ch)
{
for (auto &s : svec)//搜索在每个向量中的字符串
{
auto it = find_char(s, c, ctr);
if (it == s.size())
cout << c <<"在:"<<s<< " 中没找到" << endl;
else
{
cout << c << "在:" << s << " 中首次在位置:" << it << " 出现:" << ctr << endl;
}
}
cout<<endl;
}
//多个int类型循环搜索
cout << "输入几组数字,以-1结束" << endl;
int num = 0;
vector<int> ivec;
while (cin>>num&& num !=-1)
{
ivec.push_back(num);
}
int arr[] = { 5,6,7,8 };
for (auto i : arr)
{
auto it = find_int(ivec.begin(),ivec.end(), i, ctr);
if (it== ivec.end())
cout<< i <<" 没有找到数据"<<endl;
else
{
cout<< i <<" 首次在位置"<<it-ivec.begin()<<" 出现:"<<ctr<<"次"<<endl;
}
}
system("pause");
return 0;
}
=====================================================================
第6章 函数 191页 const形参与实参
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <cstddef>
using namespace std;
void fcn(const int i){/*fcn能够读取i,但是不能向i写值*/}
//void fcn(int i){/*错误重定义了fcn(int)*/ }
int main()
{
//const 形参和实参
const int ci = 42;//不能改变ci,const是顶层
int i = ci;//拷贝ci时,忽略了它的顶层
int* const p = &i;//const是顶层的,不能给p赋值(不能重新指向)
*p = 0;//正确,通过p改变对象的内容是允许的,现在i变成了0
system("pause");
return 0;
}
=====================================================================
#include <iostream>
#include <string>
#include <cstddef>
using namespace std;
void fun()
{//指针或引用形参与const
int i = 42;
const int *cp = &i;//正确;但是不能经由cp不能改变i,比如*cp=3只能用非const
const int &r = i;//正确,但是不能经由r改变i的值,比如r=3;只能用非const
const int &r2 = 42;//正确
//int *p = cp;//错误,p的类型与cp的类型不匹配参见第56页
//int &r3 = r;//错误.r3与r的类型不匹配
//int &r4 = 42;//错误不能用字面值去初始化一个非常量引用
}
void reset(int *i)
{
*i = 0;
}
void reset(int& i)//函数重载
{
i = 0;
}
string::size_type find_char(const string& s, char c, string::size_type &occurs)
{
auto res = s.size();
occurs = 0;
for (decltype(s.size()) i = 0; i != s.size(); ++i)
{
if (s[i] == c)
{
if (res == s.size())
res = i;
++occurs;
}
}
return res;
}
int main()
{
fun();
int i = 0;
const int ci = i;
string::size_type ctr = 0;
reset(&i);
//reset(&ci);//错误不能指向const int 对象的指针初始化
reset(i);//调用形参类型是int&的reset函数
// reset(ci);//错误,不能把普通引用绑定到const 对象ci上
// reset(42);//错误,不能把变通应该绑定到字面值上
// reset(ctr);//错误,类型不匹配,ctr是无符号类型
find_char("Hello World", 'o', ctr);//正确:find_char的第一个形参是对常的引用
system("pause");
return 0;
}
=====================================================================
第6章 函数 192页 尽量使用常量引用
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <cstddef>
using namespace std;
string::size_type find_char(const string& s, char c, string::size_type &occurs)//非const形参将出错
{
auto res = s.size();
occurs = 0;
for (decltype(s.size()) i = 0; i != s.size(); ++i)
{
if (s[i] == c)
{
if (res == s.size())
res = i;
++occurs;
}
}
return res;
}
string::size_type find_char(string& s, char c, string::size_type &occurs)//非const形参将出错
{
auto res = s.size();
occurs = 0;
for (decltype(s.size()) i = 0; i != s.size(); ++i)
{
if (s[i] == c)
{
if (res == s.size())
res = i;
++occurs;
}
}
return res;
}
bool is_stentence(const string& s)
{
//如果在s的末尾有且只有一个句号,则s是一个句子
string::size_type ctr = 0;
return find_char(s, '.', ctr) == s.size() - 1 && ctr == 1;
}//如果find_char的第一个形参类型是string& 那么上面这条调用find_char的语句没有重载
//const 版本的find_char的话就会报错,原因在于s是常量引用,但非const版本的find_char
//被不正确的定义成只能接受普通版本
//解决方法修改is_sentence的形参改成非const版本,但是这么做只是转移了错误,结果is_sentence
//只能接受非const的string对象
//正确的思路是改正find_char函数的形参,如果实在不修改就在is_sentence内部定义一个string
//类型的变量,令其为s的副本,然后把这个对象传给find_char
int main()
{
size_t ctr = 0;
find_char("Hello World", 'o', ctr);//正确:find_char的第一个形参是对常的引用
is_stentence("Hello World.");
system("pause");
return 0;
}
=====================================================================
第6章 函数 192页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
//练习6.16:下面的这个函数虽然合法,但是不算特别有用。指出它的局限性并设法改善。
bool is_empty(string& s)//应该改成const常量:const string& s
{
return s.empty();
}
bool strUpper(const string& buf)
{
for (auto c : buf)
if (isupper(c))
return true;
return false;
}
string& ToLower_(string& s)
{
for (auto &c : s)
c = tolower(c);
return s;
}
int main()
{
/*练习6.17:编写一个函数,判断string对象中是否含有大写字母。编写另一个函数,把
string对象全都改写成小写形式。在这两个函数中你使用的形参类型相同吗?为什么?*/
string buf = "ABCDEF";
if (strUpper(buf))
cout<<buf<<"含有大写字母"<<endl;
else
cout<<buf<<"不含大写字符"<<endl;
cout<<"输出:"<<ToLower_(buf)<<endl;
system("pause");
return 0;
}
//QQ108201645编写
=====================================================================
第6章 函数 193页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <cctype>
#include <vector>
using namespace std;
/*
练习6.18:为下面的函数编写函数声明,从给定的名字中推测函数具备的功能。
(a) 名为 compare 的函数,返回布尔值,两个参数都是 matrix 类的引用。
(b) 名为 change_val 的函数,返回vector<int>的迭代器,有两个参数:一个是
int,另一个是vector<int>的迭代器。
*/
class matrix
{
const string buf_ = "";
public:
matrix(const string& buf) :buf_(buf) {}//构造函数列表初始化
friend bool operator==(const matrix& s1,const matrix& s2)
{
return (s1.buf_.compare(s2.buf_));
}
bool operator==(matrix& s1)//非const调用const版本
{
return static_cast<const matrix&>(*this)== s1;
}
};
bool Compare(matrix& s1, matrix &s2)
{
return s1 == s2;
}
bool Compare(const matrix& s1,const matrix &s2)
{
return s1 == s2;
}
vector<int>::const_iterator change_val(int val, vector<int>::iterator it)
{
*it = val;
return it;
}
int main()
{
matrix s1("abc");
matrix s2("def");
cout<<Compare(s1,s2)<<endl;
const matrix s3("ccc"),s4("aaa");
cout<<Compare(s4,s3)<<endl;
vector<int> ivec{ 1,2,3,4,5 };
int value = 5;
cout<<"输出ivec向量里的元素"<<endl;
for (auto i : ivec)
cout << i << " ";
cout << "\n输出ivec向量里改变后的元素" << endl;
for (auto i = ivec.begin(); i != ivec.end(); ++i)
{
change_val(value, i);
cout << *i << " ";
}
cout<<endl;
#ifdef DECLARATION
练习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:引用形参什么时候应该是常量引用?如果形参应该是常量引用,而我们将其
设为了普通引用,会发生什么情况?;
当不需要改变实参的值或者是个字面值类型时应该是常量引用.如果改为普通引用,
则只能绑定非const实参, 且实参需要是左值.
#endif
system("pause");
return 0;
}
=====================================================================
第6章 函数 193页 数组形参
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
#include <cctype>
#include <vector>
using namespace std;
//尽管形式不同,但这三个print函数是等价的
//每个函数都有一个const int* 类型的形参
//数组形参
//void print(const int*) { ; }
//void print(const int[]);//可以看出来,函数的意图是作用于一个数组
//void print(const int[10]);//这里的维度表示我们期望数组含有多少元素,实际不一定
//使用标记指定数组长度
void print(const char* cp)
{
if (cp)//若cp不是一个空指针
while (*cp)//只要指针所指的字符不是空字符
{
cout << *cp++;//输出当前字符并将指针向前移动一个位置
}
}
void print(const int* beg, const int *end)
{
//输出beg到end之间(不含end)的所有元素
while (beg!=end)
{
cout<<*beg++<<endl;//输出当前元素并将指针向前移动一个位置
}
}
//显示传递一个表示数组大小的形参
//const int ia[]等价于是const int *ia
//size表示数组的大小,将它显示的传给函数用于控制对ia元素的访问
void print(const int ia[], size_t size)
{
for (size_t i=0;i!=size;++i)
cout<<ia[i]<<endl;
}
//数组引用形参
void print(int(&arr)[10])
{
for (auto elem:arr)
cout<<elem<<endl;
}
//将matrix声明成指向含有10个整数的数组指针
void print(int (*matrix)[10], int rowSize)
{//等价定义void print(int matrix[][10],int rowSize)
;
}
//int *matrix[10];//10个指针构成的数组
//int (*matrix)[10];//指向含胡10个整数的数组的指针
int main()
{
int i = 0,j[2] = { 0,1 };
//print(&i);//正确,&i的类型是int*
//print(j);//正确:j转换成了int *并指向j[0]
//如果我们传给print函数的是一个数组.则实参自动的转换成指向数组首元素的指针.数组的大小对函数的调用没有影响
//和其它使用数组的代码一样,以数组作为形参的函数也必须确保使用数组时不会越界
print(begin(j), end(j));//begin()和end()函数在第106页iterator头文件中
int k[10] = { 0,1,2,3,4,5,6,7,8,9 };
print(k);//当定义这个后,要注释掉第一个函数,第二个与第三个,还有main函数中的第二个与第三个,
//否则重载函数出错
system("pause");
return 0;
}
=====================================================================
第6章 函数 196页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
using namespace std;
/*
练习6.21:编写一个函数,令其接受两个参数:一个是int型的数,另一个是int指
针。函数比较int的值和指针所指的值,返回较大的那个。在该函数中指针的类型应
该是什么?*/
bool compare_(int num, int *n)
{
return num > *n ? num : *n;
}
//练习6.22:编写一个函数,令其交换两个int指针。
void swap(int* a, int *b)
{
int t = *a;
* a = *b;
*b = t;
}
int main()
{
system("pause");
return 0;
}
=====================================================================
第6章 函数 196页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
/*
练习6.25:编写一个main函数,令其接受两个实参。把实参的内容连接成一个string
对象并输出出来。*/
string& Cat(string &s, string& s1)
{
return (s += s1);
}
int main(int argc,char const* argv[])
{
string buf = "123", str = "abc";
cout<<Cat(buf,str)<<endl;
argv[0] = "prog";
argv[1] = "-d";
argv[2] = "-o";
argv[3] = "ofile";
argv[4] = "data0";
argv[5] = 0;
/*当使用argv的实参时,一定要记得可选的实参从argv[1]开始;argv[0]
保存程序的名字,而非用户输入*/
system("pause");
return 0;
}
=====================================================================
//initializer_list形参
//QQ108201645编写
表6.1: initializer_list提供的操作 | |
initializer_list<T> lst |
默认初始化: T类型元素的空列表 |
initializer_list<T> lst<a,b,c…>; |
lst的元素数量和初始值一样多;lst的元素是对应初始会的副本,列表中的元素是const |
lst2(lst) lst2=lst |
拷贝或赋值一个initializer_list对象不会拷贝列表中的元素;拷贝后原始列表和副本共享元素 |
lst.size() |
列表中的元素个数 |
lst.begin() |
返回指向lst中首元素的指针 |
lst.end |
返回指向lst中尾元素下一位置的指针 |
=====================================================================
第6章 函数 198页 initializer_list使用
=====================================================================
//QQ108201645编写
#include<iostream>
#include <string>
#include <initializer_list>
#include <sstream>
using namespace std;
class ErrCode
{
public:
ErrCode(int i) : num(i) { } // 构造函数列表初始化方式
string msg() // 成员函数,返回一个string
{
ostringstream s;//使用包含头文件#include <sstream>
s << "ErrCode " << num;
return s.str();
}
int num; // 数据成员
};
//和vector一样,initializer_list也是一种模板类型参见3.3节第86页
//定义initializer_list对象时,必须说明列表中所有元素的类型
#ifdef INITIALIZER_LIST
void error_msg(initializer_list<string> il)
{
for (auto beg = il.begin(); beg != il.end(); ++beg)
cout << *beg << endl;
cout<<endl;
}
#else
//改写如下
void error_msg(ErrCode e, initializer_list<string> il)
{
cout << e.msg() << ": ";
for (const auto &elem : il)
cout << elem << " ";
cout << endl;
}
#endif
void error_msg(const string* beg, const string* end)
{
while (beg!=end)
{
cout << *beg++ << " ";
}
cout<<endl;
}
void error_msg(ErrCode e,const string* beg, const string* end)
{
cout << e.msg() << ": ";
while (beg != end)
{
cout << *beg++ << " ";
}
cout << endl;
}
int main()
{//initializer_list中的元素永远是常量
initializer_list<string> ls{ "abc","def" };//initializer_list string
initializer_list<int> li;//initializer_list中的元素是int中的元素是
//error_msg(ls);
string expected = "description", actual = "some other case";
#ifdef INITIALIZER_LIST
if (expected != actual)
error_msg({ "functionX",expected,actual });
else
error_msg({"functionX","okay" });
#else
//改写为
if (expected != actual)
error_msg(ErrCode(42), { "functionX",expected,actual });
else
error_msg(ErrCode(0), { "functionX","okay" });
#endif
const string arr[] = { "functionX","okay" };
error_msg(begin(arr), end(arr));
error_msg(ErrCode(5), begin(arr), end(arr));
system("pause");
return 0;
}
=====================================================================
第6章 函数 200页 返回类型与return语句
=====================================================================
//QQ108201645编写
#include<iostream>
using namespace std;
void swap(int &v1, int &v2)
{//如果两个值相同,不需要交换直接退出
if (v1 == v2)
return;
int tmp = v2;//如果程序执行到这里,说明还需要交换功能
v2 = v1;
v1 = tmp;
//因为是void无返回值的,此处无须显示的return 语句
}
#ifdef DECLARATION
bool str_subrange(const string &str1, const string& str2)
{
//大小相同,此时用普通通的相等性判断结果
if (str1.size() == str2.size())
return str1 == str2;//正确:==运算符返回布尔值
//得到较短string对象的大小,条件运算符参见4.7节,134页
auto size = (str1.size() < str2.size()) ? str1.size() : str2.size();
//检查两个string对象的对应字符是否相等,以较短的字符串长度为限
for (decltype(size) i = 0; i != size; ++i)
if (str1[i] != str2[i])
return;//错误没有返回值
}
#endif
//如果ctr的值大于1,返回word的复数形式
string make_plural(size_t ctr, const string &word,
const string &ending)
{//该函数返回是string意味返回值将被拷贝到调用点,所以函数返回word的副本或临时string对象,内容是word和ending的和
return (ctr > 1) ? word + ending : word;
}
//挑出两个string对象中较短的那个,返回其引用
const string& shorterString(const string& s1, const string& s2)
{//形参与返回类型都是const string的引用,不管调用函数还是返回都不会拷贝string对象
return s1.size() <= s2.size() ? s1 : s2;
}
#ifdef DECLARATION
const string &manip()
{
string ret;
//以某种方式改变一下ret
if (!ret.empty())
return ret;//错误,返回局部对象的引用
else
return "Empty";//错误,"Empty"是一个局部临时变量
}
#endif
int main()
{
string s1 = "abc", s2 = "12345";
auto sz = shorterString(s1, s2).size();
system("pause");
return 0;
}
=====================================================================
第6章 函数 202页 引用返回左值
=====================================================================
//QQ108201645编写
#include<iostream>
#include <string>
using namespace std;
const string& shorterString(const string& s1, const string& s2)
{//形参与返回类型都是const string的引用,不管调用函数还是返回都不会拷贝string对象
return s1.size() <= s2.size() ? s1 : s2;
}
char &get_val(string &str, string::size_type ix)
{
return str[ix];//get_val假定索引值是有效的
}
int main()
{
string s("a value");
cout<<s<<endl;//输出a value
get_val(s, 0) = 'A';//调用&get_val返回一个s[ix]位置.并将s[0]的值改为A
cout<<s<<endl;//输出A value
//shorterString("hi", "bye") = "X";//错误:返回值是个常量
system("pause");//暂停
return 0;
}
=====================================================================
第6章 函数 203页 主函数main的返回值
=====================================================================
//QQ108201645编写
#include <iostream>
#include <cstdlib>
using namespace std;
//主函数的返回类型不是void,所以必须要有一个返回值
//我们允许main函数没有return语句直接结束,如果处理到尾部没有return
//编译器隐式的插入一条返回0的return 语句
int main()
{
bool some_failure = false;
if (some_failure)
return EXIT_FAILURE;//定义在cstlib头文件中
else
return EXIT_SUCCESS;//定义在cstlib头文件中
}
=====================================================================
第6章 函数 204页 递归
=====================================================================
//QQ108201645编写
#include <iostream>
using namespace std;
int factorial(int val)
{//计算val的阶乘,即1*2*3...*val
if (val > 1)
return factorial(val - 1)*val;
return 1;
}
//如果一个函数调用了它自身,不管是直接还是间接调用,都称为递归(recursive)
//在递归中(recursive),一定有某条路径是不包含递归调用的,否则将永远递归下去,调用
//自身直到程序栈空间耗尽为止,我们有时候会说这种函数含有递归循环(recursive loop)
int main()
{
cout<<factorial(5)<<endl;
system("pause");
return 0;
}
=====================================================================
factorial(5)的执行轨迹 | ||
factorial(5) |
factorial(4)*5 |
120 |
factorial(4) |
factorial(3)*4 |
24 |
factorial(3) |
factorial(2)*3 |
6 |
factorial(2) |
factorial(1)*2 |
2 |
factorial(1) |
1 |
1 |
第6章 函数 202页 引用返回左值
=====================================================================
//QQ108201645编写
#ifdef DECLARATION
练习6.30:编译第200页的str_subrange函数,看看你的编译器是如何处理函数中
的错误的。
error : non - void function 'str_subrange' should return a value
练习6.31:什么情况下返回的引用无效?什么情况下返回常量的引用无效?
当返回的是局部变量的引用时无效, 当需要修改变量时, 返回常量引用无效.
练习6.32:下面的函数合法吗?如果合法,说明其功能;如果不合法,修改其中的错误并解释原因。
int &get(int *array, int index)
{
return array[index];
}
int main()
{
int ia[10];
for (int i = 0; i != 10; ++i)
get(ia, i) = i;
}
合法, 因为返回的引用是对指针解引用后的值的引用, 解引用后的值是非局部变量.
#endif
#include <iostream>
#include <vector>
using namespace std;
//练习6.33:编写一个迭代器的递归函数,输出vector对象的内容。
void Recursive_vec(vector<int>::const_iterator beg,
vector<int>::const_iterator end)
{
if (beg == end)
return;
cout << *beg << " ";
return Recursive_vec(++beg, end);
}
#ifdef DECLARATION
练习6.34:如果factorial 函数的停止条件如下所示,将发生什么情况?
if (val != 0)
当val等于0时返回1进行阶乘
#endif
int factorial(int val)
{
if (val != 0)
return factorial(val - 1)*val;
return 1;
}
#ifdef DECLARATION
练习6.35:在调用factorial 函数时,为什么我们传入的值是 val - 1 而非 val--
当调用val--时,是先调用val的值,然后再减,变成了无限循环
#endif
int main()
{
vector<int> ivec{1, 2, 3, 4, 5, 6};
Recursive_vec(begin(ivec), end(ivec));
cout<<endl;
cout<<factorial(5)<<endl;
system("pause");
return 0;
}
=====================================================================
第6章 函数 205页 返回数组指针的函数
=====================================================================
//QQ108201645编写
#include <iostream>
using namespace std;
//下面的函数返回一个指针
int odd[] = { 1,3,5,7,9 };
int even[] = { 0,2,4,6,8 };
decltype(odd) *arrPtr(int i);
int main()
{
int arr[10];//arr是一个含有10个整数的数组
int *p1[10];//p1是一个含有10个指针的数组
int(*p2)[10] = &arr;//p2是一个指针,它指向含有10个整数的数组
//Type(*function(parameter_list))[dimension] 译:类型(*函数(规范_列表))[空间]
#ifdef DECLARATION
类似于其他数组的声明,Type表示元素的类型,dimension表示数组的大小,
(*function(parameter_list))两端的括号必须存在,就像我们定义p2时两端有括号一样,如果没有括号
,函数的返回类型将是指针的数组
int(*fun(int i))[10];
func(int i);//表示调用func函数时需要一个int类型的实参
(*func(int i));//表示可以对函数调用的结果执行解引用操作
(*func(int i))[10];//表示解引用func的调用将得到一个大小是10的数组
int(*func(int i))[10];//表示数组中的元素是int类型
#endif
auto func(int i)->int(*)[];
cout<<typeid(func).name()<<endl;//int (* __cdecl(int))[0]
//func接受一个int类型的实参,返回一个指针,该指针指向含有10整数的数组
//使用decltype
int num = 5;
cout<<arrPtr(5)<<endl;
//arrPtr使用关键字decltype表示它的返回类型是个指针,并且该指针所指向的对象与odd的类型一致
//因为odd是数组,所以arrPtr返回一个指向含有5个整数数组的指针.有一个地方要注意,decltype并不
//负责把数组类型转换成对应的指针,所以decltype结果是个数组,要想表示arrPtr返回指针必须在函数声明时
//加一个*符号
system("pause");
return 0;
}
decltype(odd) *arrPtr(int i)
{
return (i % 2) ? &odd : &even;//返回一个指向数组的指针
}
=====================================================================
第6章 函数 206页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
/*
练习6.36:编写一个函数的声明,使其返回数组的引用并且该数组包含10个string
对象。不用使用尾置返回类型、decltype或者类型别名。*/
string(&func(string s))[10];
/*
练习6.37:为上一题的函数再写三个声明,一个使用类型别名,另一个使用尾置返回类型,最后一个使用
decltype关键字。你觉得哪种形式最好?为什么?*/
//练习6.38:修改arrPtr函数,使其返回数组的引用。
int odd[] = { 1,3,5,7,9 };
int even[] = { 0,2,4,6,8 };
#ifdef DECLARATION
decltype(odd) *arrPtr(int i)
{
return(i % 2) ? &odd : &even;
}
#endif
//改成
decltype(odd)& arrPtr(int i)
{
return (i % 2) ? odd : even;
}
int main()
{
string(&Func(string s))[10];
typedef string sPtr[10];
using sPtr = string[10];
sPtr &Fun(string s);
string str[10];
decltype(str) &func(string s);
auto fun(string s)->string(&)[];
//练习6.38:修改arrPtr函数,使其返回数组的引用。
//cout<<(*arrPtr(4))[3]<<endl;
cout<<(&arrPtr(4))[3]<<endl;
system("pause");
return 0;
}
=====================================================================
第6章 函数 207页 函数重载
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
//函数重载overloaded
void print(const char* cp) {}
void print(const int *beg, const int *end){}
void print(const int ia[], size_t size){}
//这些函数接受的形参不一样,但是执行的操作非常类似,当调用时,
//编译器根据传递的实参类型推断想要的是哪个函数
//main()函数不能重载
#ifdef DECLARATION
//一种典型的数据库应用
Record lookup(const Account&);//根据Account查找记录
Record lookup(const Phone&);//根据Phone查找记录
Record lookup(const Name&);// 根据Name查找记录
Account acct;
Phone phone;
Record r1 = lookup(acct);//调用接受Account
Record r2 = lookup(phone);
Record lookup(const Account&);
bool lookup(const Account&);//错误,与上一个函数相比只有返回类型不同
Record lookup(const Account&acct);
Record lookup(const Account&);//只是省略了形参的名字
typedef Phone Telno;
Record lookup(const Phone&);
Record lookup(const Telno&);//Telno和Phone的类型相同
//重载和const 形参
//下面4个的每一组的第二个声明和第一个是等价的
Record lookup(Phone);
Record lookup(const Phone);//重复声明了Record lookup(const Phone)
Record lookup(Phone*);
Record lookup(Phone* const);//重复声明了Record lookup(Phone* const)
//对于接受引用或指针的函数来说,对象是常量还是非常量对应的形参不同
//定义了4个独立的重载函数
Record lookup(Account&);//函数作用于Account的引用
Record lookup(const Account&);//新函数,作用于常量引用
Record lookup(Account*);//新函数,作用于指向Account的指针
Record lookup(const Account*);//新函数,作用于指向常量的指针
#endif
#ifdef DECLARATION
//在这种情况使用重载函数
Screen& moveHome();
Screen& moveAbs(int ,int );
Screen& moveRel(int ,int ,string direction);
//看上去似乎可以把这组统一命名为move,
Screen& move();
Screen& move(int, int);
Screen& move(int, int, string direction);
//实际重载之后函数失去了名字中本来拥有的信息
//哪种更容易理解呢
Screen& moveHome();//我们认为是这一个
Screen& move();
#endif
int main()
{
int j[2] = { 0,1 };
print("Hello World");//调用void print(const char* cp);
print(j, end(j) - begin(j));//调用void print(const int ia[], size_t size);
print(begin(j), end(j));//调用void print(const int *beg, const int *end);
system("pause");
return 0;
}
=====================================================================
第6章 函数 209页 const_cast和重载
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
//比较两个string对象的长度,返回较短的那个引用
const string &shorterString(const string& s1, const string& s2)
{
return s1.size() <= s2.size() ? s1 : s2;
}
//这具函数的参数与返回类型都是const string的引用
string& shorterString(string& s1, string& s2)
{
auto &r = shorterString(const_cast<const string&>(s1),
const_cast<const string&>(s2));//用强制转换调用const版本的比较
return const_cast<string&>(r);
}
#ifdef DECLARATION
练习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)非法, 顶层const无法与没有顶层const的形参区分开来
(b)非法, 函数重载不通过返回值来区分
(c)合法
#endif
string read();
void print(int i){}//强调有这个函数才能编译通过
void print(const string&);
void print(double d){ ; }//重载print
void fooBar(int ival)
{
bool read = false;//新作用域,隐藏了外层的read
// string s = read();//错误,read是一个布尔值,而非函数
//在局部作用域中声明函数不好
void print(int i);//新作用域,隐藏了之前的print(强调新函数)
//print("Value: ");//错误,print(const string& )被隐藏掉了
print(ival);//正确:当前print(int)可见
print(3.14);//正确,调用print(int);print(double)被隐藏掉了
}
int main()
{
system("pause");
return 0;
}
=====================================================================
第6章 函数 212页
=====================================================================
//QQ108201645编写
#include<iostream>
#include<string>
using namespace std;
typedef string::size_type sz;//关于typedef参见2.51节
//string screen(sz ht = 24, sz wid = 80, char backgrnd = ' ') { string s1 = "abc"; return s1; }
string screen(sz = 24, sz = 80, char backgrnd = ' ') { string s1 = "abc"; return s1; }//任一个都可以
#ifdef DECLARATION
//表示高度和宽度的形参没有默认值
string screen(sz, sz, char = ' ') { string s1 = "abc"; return s1; };//错误重复声明
#endif
#ifdef DECLARATION
//默认实参初始值
sz wd = 80;
char def = ' ';
sz ht() { return 0; }
string screen(sz=ht(), sz=ht(), char =def) { string s1 = "abc"; return s1; }
string window = screen();//调用screen(ht(),80,' ');
#endif
string window;//定义在此处方便调用
char def = ' ';//定义
void f2()
{
def = '*';//改变实参的值
sz wd = 100;//隐藏了外层定义的wd,但是没有改变默认值
window = screen();//调用screen(ht(),80,' ');
}//在函数f2内部改变了def的值,所以screen的调用将会传递这个更新过的值
//另一方面,函数声明了一个局部用于隐藏外层的wd,但是该局部变量
//与传递给予screen的默认参数没有任何关系
int main()
{
window = screen();//等价于screen(24,80,' ')
window = screen(66);//等价于screen(66,80,' ')
window = screen(66, 256, '#');//等价于screen(66,80,'#')
//window = screen(, , '?');//错误只能省略尾部的实参
window = screen('?');//调用screen('?',80,' ')
system("pause");
return 0;
}
=====================================================================
第6章 函数 213页 练习题
=====================================================================
//QQ108201645编写
#ifdef DECLARATION
练习6.40:下面的哪个声明是错误的?为什么?
(a) int ff(int a, int b = 0, int c = 0);
(b) char *init(int ht = 24, int wd, char bckgrnd);
(b)的声明是错误的, 因为一旦声明一个带默认值的形参, 则其后面声明的形参都必须带有默认值.
练习6.41:下面的哪个调用是非法的?为什么?哪个调用虽然合法但显然与程序员的初衷不符?为什么?
char *init(int ht, int wd = 80, char bckgrnd = ' ');
(a)init();
(b)init(24, 10);
(c)init(14, '*');
(a)调用非法, 因为参数数量没有匹配成功.
(c)与程序的初衷不符, 第二个实参将不会与第三个形参结合, 而是与第二个形参进行自动提升转换后结合.
练习6.42:给make_plural函数(参见6.3.2节,第201页)的第二个形参赋予默认实参's', 利用新版本的函数输出单词success和failure的单数和复数形式。
#endif
#include <iostream>
#include<string>
using namespace std;
string make_plural(size_t ctr, const string &word, const string &ending = "s")
{
return (ctr > 1) ? word + ending : word;
}
int main(int argc, char const *argv[])
{
cout << make_plural(1, "success") << endl;
cout << make_plural(5, "success", "es") << endl;
cout << make_plural(1, "failure") << endl;
cout << make_plural(5, "failure", "") << endl;
return 0;
}
=====================================================================
第6章 函数 213页 内联函数和constexpr函数
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
//内联函数,寻找两个string对象中较短的那个
inline const string& shorterString(
const string& s1, const string& s2)
{
return s1.size() <= s2.size() ? s1 : s2;
}
//constepxr函数
constexpr int new_sz()
{
return 42;
};
constexpr int foo = new_sz();//正确:foo是一个常量表达式
constexpr size_t scale(size_t cnt)
{
return new_sz()*cnt;
}
int arr[scale(2)];//正确:sales(2)是常量表达式
//int i=2;//i不是常量表达式
//必须改成const
size_t i = 2;
//如果上面未修改,就是错误,scale(i)不是常量表达式
int a2[scale(i)];
//如果用非常量表达式调用scale,则返回一个非常量表达式,则结果不是常量表达式的话,将报错
//constexpr返回不一定是常量表达式
int main()
{
string s1 = "abc";
string s2="12345";
cout<<shorterString(s1,s2)<<endl;
//内联函数可以避免函数调用的开销.调用上面的函数将在编译过程展开成如下面这句
cout << (s1.size() < s2.size() ? s1 : s2) << endl;
}
#ifdef DECLARATION
练习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:回顾在前面的练习中你编写的那些函数,它们应该是内联函数吗?如果是,将它们改写成内联函数;如果不是,说明原因。
视情况而定, 对于频繁调用, 流程直接切规模较小的函数则应该改写成内联函数.
练习6.46:能把isShorter函数定义成constexpr函数吗?如果能,将它改写成constexpr函数;如果不能,说明原因。
可以, 在不要求常量表达式的上下文中时, 可以定义成constexpr.
#endif
=====================================================================
第6章 函数 216页 调试帮助
=====================================================================
//QQ108201645编写
#include<iostream>
using namespace std;
/*
assert预处理宏:
assert(expr);对expr求值,结果为0,终止程序,非0就什么也不做
*/
#ifdef DECLARATION
__FILE__;//存放文件名的字符串字面值
__LINE__;//存放当前行号的整形字面值
__TIME__;//存放文件编译时间的字符串字面值
__DATE__;//存放文件编译日期的字符串字面值
#endif
#define NDEBUG
void print(const int ia[], size_t size)
{
#ifdef NDEBUG
//__func__是编译器定义的一个局部静态变量,用于存放函数的名字
cerr << __func__ << ": array size is " << size << endl;//使用__func__输出函数的名字
#endif
}
int main()
{
int num[] = { 1,2,3,4,5 };
print(num, 5);
string word = "abc";
size_t threshold=6;
if (word.size() < threshold)
cerr << "Error: " << __FILE__ //输出文件名字符串字面值
<< " : in function " << __func__ //输出main函数
<< " at line " << __LINE__ << endl //输出所在行数
<< " Compiled on " << __DATE__ // 输出编译的日期
<< " at " << __TIME__ << endl //输出编译的时间
<< "\":Length too short" << endl;
system("pause");
return 0;
}
=====================================================================
第6章 函数 217页 练习题
=====================================================================
#ifdef DECLARATION
练习6.47:改写6.3.2节(第205页)练习中使用递归输出vector内容的程序,使其
有条件地输出与执行过程有关的信息。例如,每次调用时输出vector对象的大小。分
别在打开和关闭调试器的情况下编译并执行这个程序。
#endif
#include <iostream>
#include <vector>
#define NDEBUG
using namespace std;
//递归函数输出vector<int>的内容
void print(vector<int>&vInt, unsigned index)
{
unsigned sz = vInt.size();
#ifdef NDEBUG
cout<<"vector对象大小是:"<<sz<<endl;
#endif
if (!vInt.empty() && index < sz)
{
cout<<vInt[index]<<endl;
print(vInt, index + 1);
}
}
void func(vector<int>::iterator beg, vector<int>::iterator end)
{
if (beg == end)
return;
cout << *beg << '\t';
#ifdef NDEBUG
cout << "vector size:" << end - beg << endl;
#endif
func(++beg, end);
}
int main(int argc, char const *argv[])
{
vector<int> v{ 1,3,5,7,9,11,13,15 };
print(v, 0);
cout<<endl;
func(v.begin(), v.end());
cout<<endl;
system("pause");
return 0;
}
#ifdef DECLARATION
练习6.48:说明下面这个循环的含义,它对assert的使用合理吗?
string s;
while (cin >> s && s != sought) {} //空函数体
assert(cin);
(assert是一种预处理宏,当assert的条件为真时什么也不做,当它的条件为假是,输出信息并终止)
循环输入s当cin还没遇到结束符或者输入的数值不等于sought的时候执行函数体,否则退出函数体
不合理。如果是cin遇到结束符,则assert会输出信息并终止程序的执行。
#endif
//QQ108201645编写
=====================================================================
第6章 函数 217页 函数匹配
=====================================================================
//QQ108201645编写
#include <iostream>
using namespace std;
void f(){}
void f(int){}
void f(int, int){}
void f(double, double = 3.14){}//第二个提供默认值
/*
f(5.6)逐一检查最匹配的函数,如果调用f(int),实参将从double转换成int.
另一个可行的就是void f(double, double = 3.14){}
*/
int main()
{
f(5.6);//调用void f(double,double)
#ifdef DECLARATION
f(42, 2.56);//匹配的有void f(int, int)和void f(double, double = 3.14),编译器逐个检查
#endif
//哪个函数是最佳匹配,如果只有一个满足条件则匹配成功.
//调用void f(int, int),则要转换2.56成int,调用另一个的话就要把42转换成double,具有二义性
cout<<"42是"<<typeid(42).name()<<"类型"<<endl;//整数默认返回一个int类型,内置的类型转换匹配低于精确匹配
#ifdef DECLARATION
该函数每个实参的匹配都不劣于其他可行函数需要的匹配
至少有一个实参的匹配优于其他可行函数提供的匹配
#endif
//练习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)
void f(int,int)
void f(double,double=3.14)
最佳匹配不存在,第一个实参2.56:void f(double,double=3.14)能匹配,但42必须转换.如果用第二个实参
42:void f(int,int)能匹配,那就要转换第一个实参2.56转换成int,最终结果可行函数在各自一具实参上
实现了更好的匹配,比较起来无法判断哪个好,因此编译器定义为具有二义性
(b)
void f(int)
void f(double,double=3.14)
第一个是最佳匹配,参数无需做任何转换
(c)
void f(int,int)
void f(double,double=3.14)
第一个是最佳匹配,参数无需做任何转换
(d)
void f(int,int)
void f(double,double=3.14)
第二个是最佳匹配,参数无需做任何转换
*/
/*练习6.51:编写函数f的4个版本,令其各输出一条可以区分的消息。验证上一个练习
的答案,如果你回答错了,反复研究本节的内容直到你弄清自己错在何处。*/
void fun();//声明函数
void fun(int);
void funf(int, int);
void fun(double, double = 3.14);//double默认实参
fun(2.56, 42);
fun(42);
fun(4.2);
fun(42, 0);
fun(2.56, 3.14);
system("pause");
return 0;
}
void fun() {cout<<"fun()..."<<endl;}//定义函数
void fun(int) { cout << "fun(int)..." << endl; }
void funf(int, int) { cout << "fun(int, int)..." << endl; }
void fun(double a, double b) { cout << "fun(double, double = 3.14)..." <<a<<" "<<b<< endl; }
=====================================================================
第6章 函数 219页 实参类型转换
=====================================================================
#ifdef DECLARATION
1精确匹配包括以下情况
实参类型与形参类型相同
实参从数组类型或函数类型转换成对应的指针类型6.7节,第221页
向实参添加顶层const或从实参删除顶层const
2通过实参添加顶层的匹配4.11.2节,第143页
3通过类型提升实现的匹配4.11.1节第142页
4通过算术类型转换(4.11.1节第142页)或指针转换(4.11.2节, 第143页)实现的匹配
5通过类类型转换实现的匹配(14.9第514页)
#endif
//QQ108201645编写
#include <iostream>
using namespace std;
int main()
{
void ff(int);
void ff(short);
ff('a');//char提升成int;调用f(int)
void manip(long);
void manip(float);
//manip(3.14);//错误二义性调用3.14既能转换成long民能转换float
#ifdef DECLARATION
Record lookup(Account&);//函数的参数是Account的引用
Record lookup(const Account&);//函数的参数是一个常量引用
const Account a;
Account b;
lookup(a);//调用Record lookup(const Account&);
lookup(b);//调用Record lookup(Account&);
#endif
//练习6.52:已知有如下声明:
void manip(int, int);
double dobj=0;
/*
请指出下列调用中每个类型转换的等级(参见6.6.1节,第219页)。
(a) manip('a', 'z');
(b)manip(55.4, dobj);
(a)(b)都将实参转换为形参类型.(a)是第3等级, (b)是第4等级
*/
manip('a', 'z');
manip(55.4, dobj);
/*
练习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)合法,两个函数的区别是它们的引用类型的形参是否引用了常量,属于底层const,可以把两个函数区分开来
(c)非法, 重载无法区分顶层const
*/
system("pause");
return 0;
}
void ff(int){cout<<__FUNCTION__<<endl;}
void ff(short) { cout << __FUNCTION__ << endl; }
void manip(long) { cout << __func__ << endl; }
void manip(float) { cout << __func__ << endl; }
void manip(int ,int ){}
=====================================================================
#include <iostream>
#include <string>
using namespace std;
//声明(不是严格来说必要的在这个文件中)
string::size_type sumLength(const string&, const string&);
string::size_type largerLength(const string&, const string&);
//这些函数的定义
string::size_type sumLength(const string &s1, const string &s2)
{
return s1.size() + s2.size();
}
string::size_type largerLength(const string &s1, const string &s2)
{
return (s1.size() > s2.size()) ? s1.size() : s2.size();
}
//根据其字符串参数的值,
//getFcn 返回一个指针sumLength或largerLength指针
//三种方法声明getFcn
//1。使用decltype返回类型,
//记住添加 * 表明getFcn返回一个指针
decltype(sumLength) *getFcn(const string &);
//使用后返回类型
auto getFcn(const string&)->string::size_type(*)(const string&, const string&);
//直接定义
string::size_type(*getFcn(const string&))(const string&, const string&);
decltype(sumLength)* getFcn(const string &fetch)
{
if (fetch == "sum")
return sumLength;
return largerLength;
}
int main()
{
cout << getFcn("sum")("hello", "world!") << endl; // 输出 11
cout << getFcn("larger")("hello", "world!") << endl; // 输出 6
system("pause");
return 0;
}
=====================================================================
第6章 函数 221页 函数指针
=====================================================================
//QQ108201645编写
#include<iostream>
using namespace std;
bool lengthCompare(const string&, const string&);
bool (*pf)(const string&, const string&);
//pf前面有个*,因此pf是指针,右侧是形参列表,表示pf是指向函数.返回一个布尔值
//pf就是一个指针函数的指针,其中参数是两个const string的引用,返回布尔类型
string::size_type sumLength(const string& ,const string&);
bool cstringCompare(const char*, const char*);
int main()
{
pf = lengthCompare;//pf指向名为lengthCompare的函数
pf = &lengthCompare;//等价的赋值语句:取地址符是可选的
//此外,我们还能直接使用指向函数的指针调用该函数,无须提前解引用
bool b1 = pf("hello", "goodbye");//调用bool lengthCompare(const string&, const string&);
bool b2=(*pf)("hello", "goodbye");//bool (*pf)(const string&, const string&);等价调用
bool b3 = lengthCompare("hello", "goodbye");//等价的,直接调用
pf = 0;//正确;pf不指向任何函数
//pf = sumLength;//错误,返回类型不匹配
//pf = cstringCompare;//错误,形参类型不匹配
pf = lengthCompare;//正确,函数和指针的类型精确匹配
system("pause");
return 0;
}
bool lengthCompare(const string& s1, const string& s2)
{
return s1.size() == s2.size();
}
=====================================================================
第6章 函数 222页 重载函数的指针
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
void ff(int*){}
void ff(unsigned int){}
void(*pf1)(unsigned int) = ff;
//void(*pf2)(int) = ff;//错误,没有任何一个ff与该形参列表匹配
//double(*pf3)(int*) = ff;//错误.ff与pf3的返回类型不同
bool lengthCompare(const string& s1, const string& s2)
{
return s1.size() == s2.size();
}
//bool(*pf)(const string&, const string&);
#ifdef DECLARATION
//第三个形参是函数形参,它会自动的转换成指向函数的指针
void useBingger(const string &s1, const string& s2,
bool pf(const string &, const string& ));
#endif
//等价的声明,显示将形参定义成指向函数的指针
void useBingger(const string &s1, const string& s2,
bool(*pf)(const string &, const string&))
{
}
int main()
{
//pf = lengthCompare;
string s1, s2;
//自动的将函数lengthCompare转换成指向该函数的指针
useBingger(s1, s2, lengthCompare);
typedef bool Func(const string &, const string&);
typedef decltype(lengthCompare) *FuncP2;//等价类型
void useBingger(const string &, const string&, Func);
void useBingger(const string &, const string&, FuncP2);
system("pause");
return 0;
}
=====================================================================
第6章 函数 223页 重载函数的指针
=====================================================================
//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;
string::size_type sumLength(const string &s1, const string &s2)
{
return s1.size() + s2.size();
}
string::size_type largerLength(const string &s1, const string &s2)
{
return (s1.size() > s2.size()) ? s1.size() : s2.size();
}
int main()
{
using F = int(int *, int);//F是函数类型,不是指针类型
using PF = int(*)(int *, int);//PF是指针类型
PF f1(int);//正确,PF是指向函数的指针,f1返回指向函数的指针
//F f1(int);//错误:F是函数类型,f1不能返回一个函数
F *f1(int );//正确,显示的指定返回类型是指向函数的指针
//也可以这样直接声明
int(*f1(int))(int*, int);
auto f1(int)->int(*)(int *, int);
string::size_type sumLength(const string&, const string&);
string::size_type largerLength(const string&, const string&);
//根据其形参的取值,getFcn函数返回指向sumLength或者largerLength的指针
decltype(sumLength) *getFcn(const string&);
cout<<getFcn("sum")("hello"," world")<<endl;
cout << getFcn("large")("hello", " world") << endl;
system("pause");
return 0;
}
decltype(sumLength) *getFcn(const string& name)
{
if (name == "sum")
return sumLength;
return largerLength;
}
=====================================================================
第6章 函数 223页 练习题
=====================================================================
//QQ108201645编写
#include <iostream>
#include <vector>
#include <cassert>
using namespace std;
//练习6.54:编写函数的声明,令其接受两个int形参并且返回类型也是int;然后
//声明一个vector对象,令其元素是指向该函数的指针。
//练习6.55:编写4个函数,分别对两个int值执行加、减、乘、除运算;在上一题创建的
//vector对象中保存指向这些函数的指针。
int func1(int i, int j) { return i + j; }
int func2(int i, int j) { return i - j; }
int func3(int i, int j) { return i * j; }
int func4(int i, int j)
{
assert(j != 0);
return i / j;
}
//练习6.56:调用上述vector对象中的每个元素并输出结果。
int main(int argc, char const *argv[])
{
int func(int, int);
vector<int(*)(int, int)> v;
#ifdef DECLARATION
//等价于下面这两条
typedef int(*FUNC)(int, int);
vector<FUNC> v;
#endif
v.push_back(func1);
v.push_back(func2);
v.push_back(func3);
v.push_back(func4);
for (auto i : v)
{
cout << i(3, 3) << '\t';//调用第一个函数并传递两个值进去
}
cout << endl;
decltype(func) *p1 = func1, *p2 = func2, *p3 = func3, *p4 = func4;
vector<decltype (func)* > v1{ p1,p2,p3,p4 };
for (auto i : v1)
{
cout << i(6, 3) << '\t';
}
cout << endl;
system("pause");
return 0;
}
}
cout << endl;
system("pause");
return 0;
}