C++ Primer第六章!
#include "stdafx.h"
#include<iostream>
#include<string>
#include<vector>
#include<initializer_list> //实参数量未知,但是类型相同
#include<cassert> //调式头文件,assert和NDEBUG
using namespace std;
int fact_sample(int val)
{
int ret = 1;
while (val > 1)
{
ret = ret*val;
--val;
}
return ret;
}
int fact_iterator(int val)
{
int tmp = val;
if(val>1)
return tmp*fact_iterator(--val); //此处不能使用val*fact_iterator(--val),val的值会改变,造成表达式逻辑错误!
else
{
return val;
}
}
int factorial(int val)
{
if (val>1)
return val*fact_iterator(val-1); //同int fact_iterator(int val)。
else
{
return 1;
}
}
int fact_for(int val)
{
int ret = 1;
for (int i = 1; i <= val; ++i)
{
ret = ret*i;
}
return ret;
}
size_t count_calls()
{
static size_t ctr = 0; //局部静态对象,直到程序终止才被销毁
return ++ctr;
}
void reference_fun(int &val)
{
--val;
}
void p_fun(int *ip)
{
*ip = *ip - 1;
}
bool isShorter(const string &s1, const string &s2) //比较两个string对象的长度,string可能非常长,建议使用引用!
{ //有的类型不支持拷贝(iostream,其同时也不支持const),则只能通过引用传递!
return s1.size() < s2.size();
}
void print_arry(const int *arry) //等价于const int[],const int[10].不允许拷贝数组,但是形参还是可以写成数组形式,其实质是const int*
{
cout << arry[0] << endl;
}
void print_char(const char *cp)
{
if (cp)
while (*cp)
cout << *cp++;
}
void print_iter(const int *beg, const int *end)
{
while (beg != end)
cout << *beg++ << endl;
}
void print_count(const int ia[], size_t size)
{
for (size_t i = 0; i != size; ++i)
{
cout << ia[i] << endl;
}
}
void print_reference(int(&arr)[2])
{
for (auto elem : arr)
cout << elem << endl;
}
void print_mulit_ptr(int(*matptr)[2],int rowsize) //指向含有10个整数的数组的指针。而int *matptr[10]表示10个指针构成的数组
// 等价于void print_mulit_ptr(intmatptr[][2],int rowsize)
{
for (int i = 0; i < rowsize; ++i)
{
auto *beg = begin(*matptr); //由于已知数组的大小,也可以直接使用。
while (beg!=end(*matptr))
{
cout << *beg << "+";
++beg;
}
cout << endl;
++matptr;
}
}
void print_msg(initializer_list<string> lst)
{
for (auto beg = lst.begin(); beg != lst.end(); ++beg) //可以使用范围for循环处理其中的元素
cout << *beg << " ";
cout << endl;
}
const string &shortString(const string &s1, const string &s2)
{
return s1.size() <= s2.size() ? s1 : s2;
}
void print_ret()
{
return;
}
char &get_value(string &str, string::size_type ix)
{
return str[ix];
}
vector<string> process_return()
{
return { "aaa","bbb","ccc" }; //也可以返回renturn {};返回指定类型空vector
}
int odd[2] = { 13,14 };
int even[2] = { 52,93 };
decltype(odd) *arrptr(int i)
{
return (i % 2) ? &odd : &even; //int (*p)[2]=&odd;指向10个整数的数组的定义。
}
void print_overload()
{
cout << "Test" << endl;;
}
void print_overload(const char *cp)
{
cout<<*cp<<endl;
}
void print_overload(const char *cp,string::size_type sz)
{
for (unsigned int i = 0; i < sz; ++i)
{
cout << cp[i];
}
cout << endl; //最好不在函数中定义输出的格式。
}
const string &shorterString(const string &s1, const string &s2)
{
return s1.size() < s2.size() ? s1 : s2;
}
string &shorterString(string &s1, string &s2)
{
auto &r = shorterString(const_cast<const string&>(s1), const_cast<const string&>(s2));
return const_cast<string&>(r);
}
using sz = string::size_type;// size_t是全局定义的类型;size_type是STL类中定义的类型属性,用以保存任意string和vector类对象的长度!数组下表是size_t,而容器是size_type!
string screen(sz ht = 24, sz wid = 80, char background = ' ') //一旦某个形参被赋予了默认值,他后面的所有形参都必须有默认值。
{
return " "; //如果进行多次声明默认实参函数,则函数的后续声明只能为没有默认实参的形参添加默认值,且该形参右侧所有形参都必须有默认值!
}
inline const int &max_numbers(const int &a, const int &b)
{
return a < b ? b : a;
}
constexpr size_t arr_count(size_t cnt)
{
return 10 * cnt;
}
int addb(const int &a, const int &b)
{
return(a + b);
}
int(*p_addb)(const int &a, const int &b); //p_addb指向函数的指针,其中该函数的两个参数为const int的引用,返回值为int。
void print_addb(ostream &os, int(*print_addb)(const int &a, const int &b),const int a=3,const int b=4)
{
os << (*p_addb)(a,b) << endl;
}
int main()
{
int j_sample = fact_sample(5); //调用函数,实参可以隐式转换为形参!(形参初始化的机理和变量初始化一样)
cout << j_sample << endl; //函数的返回类型不能是数组和函数,但可以是指向数组或函数的指针
int j_iterator = fact_iterator(5); //在头文件中放函数声明,在源文件中包含头文件,并定义函数。
cout << j_iterator << endl;
int j_for = fact_for(5);
cout << j_for << endl;
int j_reference = 5;
reference_fun(j_reference); //引用传递,形参初始化的机理和变量初始化一样
cout << j_reference << endl;
int j_p = 5; //指针传递,形参初始化的机理和变量初始化一样
p_fun(&j_p);
cout << j_p << endl;
//void fcn(const int i){}
//void fcn(int i){} 两个函数是一样的,在形参拷贝初始化时,忽略了它的顶层const,因此给函数形参是一样的!
//C++允许通过字面值初始化常量引用,而不允许初始化普通引用!
int i_arry[10] = { 0,1,2,3 };
print_arry(i_arry);
//使用数组时确保不会越界,常见3种常用的技术
//数组本身包含一个结束标记,例如C风格字符串
char *p_char = "abc";
print_char(p_char);
//begin和end函数
int j_arry[2] = { 1,2 };
print_iter(begin(j_arry), end(j_arry));
//显示传递一个数组大小
int j_count[2] = { 9,8 };
print_count(j_count, end(j_count) - begin(j_count));
//数组引用形参
int j_refer[2] = { 7,6};
print_reference(j_refer);
//传递多维数组
int j_mulit_arry[2][2] = { 1,2,3,4 };
print_mulit_ptr(j_mulit_arry, 2);
//可变形参的函数,1、如果实参类型相同可以使用initializer_list;2、可变参数模版?
//initializer_list<T> lst;
//initializer_list<T> lst{a,b,c}; lst对象中的元素永远是常量值,即abc为const。
//lst2=lst; 拷贝或赋值一个initializer_list对象不会拷贝列表中的元素,原始列表和副本共享元素。
//lst.size();
//lst.begin(); 返回指向lst中首元素的指针。
//lst.end();
print_msg({ "aa","bb","cc" }); //向initializer_list形参中传递一个值的序列,必须把序列放在一对花括号内。
//返回return
//无返回值函数,可以使用return语句提前退出函数
print_ret();
//有返回值函数,返回类型应与函数类型一致。返回一个值的方式和初始化一个变量或形参的方式完全一样!
string s_short1 = "aaa", s_short2 = "bbbb";
cout << shortString(s_short1, s_short2) << endl;
//返回局部变量时,则返回的是局部变量的副本(未命名临时变量)。请一定不要返回局部对象的引用或指针!!!
//引用返回值,返回引用的函数得到左值,其他返回类型得到右值
string s_value("aaaaa");
get_value(s_value, 2) = 'B';
cout << s_value << endl;
//列表初始化返回值
vector<string> err_msg_return = process_return();
//主函数main的返回值
//mian函数的返回值可以看作状态指示器,返回0表示执行成功,返回其他值表示失败。
//cstdlib中定义了两个预处理变量,EXIT_FAILURE,EXIT_SUCCESS。
//递归,函数调用自身,递归函数
cout << factorial(5) << endl;
// 返回数组指针
using arrT = int[2]; //等价于typedef int arrT[10]
//arrT* func(int i),等价于int (*func(int i))[10],也可以使用C++新规定:位置返回类型
//auto func(int i) -> int(*)[10];
//如果已知函数返回的指针将指向哪个数组,可以使用decltype推断
int i_decl = 2;
int (*out_decl)[2] = arrptr(i_decl);
for (int i = 0; i < 2; ++i)
{
cout << (*out_decl)[i] << " "; //int (*p)[2]=&odd;p为指针,指向10个整数的数组。
}
cout << endl;
//重载函数,在形参数量或类型上有所不同。尽量只重载那些确实非常相似的操作
const char *p_overload = "abcdefg";
print_overload();
print_overload(p_overload);
print_overload(p_overload, 3);
//顶层const形参,函数等价。底层const,函数可重载。顶层const只决定了在函数体内是否可以改变形参值,而不影响传入的实参。
//int lookup(int),int lookup(const int); int lookup(int*),int lookup(int *const),顶层const等价
//int lookup(int&),int lookup(const int&); int lookup(int*),int lookup(const int*),如果形参是某种类型的指针或引用,则通过区分其指向的是常量对象还是非常量对象可以实现函数重载(底层const)。
//const_cast和重载
//shorterString,有什么特别作用吗?当使用非常量实参,想得到非常量的引用时,此时使用shorterString,定义2个重载函数。同时,重载函数应该避免强制类型转换!
//默认实参,调用含有默认实参的函数,可以包含该实参,也可以省略该实参。
string window;
window = screen();
window = screen(255, 255, '+');
//window=screen(,,'+') 错误行为,在设计默认实参函数时,尽量让使用默认值的形参放在后面,不适用默认值的形参放在前面!
//默认实参初始值含义?
//内联函数,适用于节省开销、流程直接、频繁调用的函数
int inline_a = 3, inline_b = 5;
cout << max_numbers(inline_a, inline_b)<<endl; //等价于cout<<(a < b ? b : a)<<endl;
//constexpr函数,函数的返回类型和所有形参的类型都得是字面值类型(内置类型、引用、指针),而函数体中必须有且只有一条return语句。
int arr_constexpr1[arr_count(2)];
size_t i_constexpr = 2;
//int arr_constexpr2[arr_count(i_constexpr)]; arr_count返回值不为常量表达式。
//调试帮助,assert和NDEBUG
assert(3 > 2); //如果表达式为假,则输出信息并终止程序,可以用#define语句定义NDEBUG语句,关闭调式状态!
//函数指针,指向函数而非对象
int a_func = 1, b_func = 2;
p_addb = addb; //等价于p_addb=&addb.另外可以为p_addb赋值nullptr,表示没有指向任何一个函数。
cout << addb(a_func, b_func) << endl;
cout << p_addb(a_func, b_func) << endl;
cout << (*p_addb)(a_func, b_func) << endl; //等价于上面调用语句
//使用函数指针作为形参, 将函数作为实参
print_addb(cout, addb);
print_addb(cout, addb,1,9);
//返回指向函数的指针
using F = int(int);
using PF = int(*)(int);
//PF f1(int),f1返回指向函数的指针
//F *f1(int),f1返回指向函数的指针
//auto f1(int)->int(*)(int),返回指向函数的指针。
//也可以使用decltype推断函数的类型,得到形如F形式,接着声明函数。
cin.ignore();
return 0;
}
//函数包含返回类型声明、函数名、形参、实参、函数体等等部分,另外可以对函数进行重载。
//内联函数可以避免常见的函数调用开销!
//省略符形参是什么鬼?