#ifndef FUNC_H
#define FUNC_H
#include<iostream>
#include<string>
#include<vector>
#include<cstdlib>
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::vector;
char &get_val(string &s, string::size_type pos);
//可以将小型函数定义为内联函数(inline function),可以减少函数调用的开销
// inline 只是向编译器请求,编译器可以选择忽略。。。
//如果要将 inline 的声明和定义分开文件,需要注意
/*
csdn 论坛:http://bbs.youkuaiyun.com/topics/100081253
我概括过inline函数的一条基本原则(不知道别人有没有类似提法):
首先介绍一个概念,“编译单元”,用不太严谨的方式定义,就是
当你把一个源文件(.cpp .cxx等)做完预处理,也就是把包含的头文件的内容全部放
到这个文件里来,所有宏都展开,等等,形成的一个逻辑上的实体——就是编译单元
一个编译单元可以单独编译,但不能链接成一个可执行程序(除非程序只有这个编译单元)
有了编译单元的概念以后,你只要确保以下这个原则就可以了:
如果在这个编译单元里使用了一个Inline函数,那么我在这个编译单元结束之前,
必须能够“看到”这个编译单元的完整定义(所有实现代码)
另外要注意,在编译单元之内,调用inline函数的代码行之前,至少要放置
一个这个inline函数的声明,当然有定义也可以
从这个原则出发,最简单的使用Inline函数的方法就是在头文件定义,
否则你要在每一个使用inline函数的编译单元里一一定义这个函数,
如果有n个编译单元,你就要把inline函数的代码重复书写n次
*/
//返回列表
inline vector<int> get_vector()
{
return{3,4,52};
}
//定义返回数组指针的函数
//1.定义类型别名
typedef int parr[5];
//using parr = int[5];
extern int arr1[5];
extern int arr2[5];
parr *func1(int);
//2. 不使用类型别名
//Type ( *function( parameter_list)) [ demension]
//Type 数组元素的类型, demension 数组的维度, function 函数名
int ( *func2( int))[ 5]; //func1 不使用类型别名
//3.使用尾置返回类型(trailing return type)
auto func3(int i) -> int(*)[5];
//4.使用已知数组和 decltype 关键字
decltype(arr1) *func4(int i);
decltype(arr1) &func5(int i);
//重载函数(overloaded):同一个作用域内函数名相同,形参列表(形参数量和类型)不同
//main函数不能重载
//不允许两个函数除返回类型以外的其他要素都相同
//顶层 const 形参和无顶层 const 的形参没有区别,拷贝初始化时忽略顶层 const
//底层 const 指针或引用,通过区分指向对象是常量还是非常量来实现函数重载
//const_cast 强制类型转换
const string &shorter(const string &s1, const string &s2);
//如果需要返回非常量
string &shorter(string &s1, string &s2);
//默认实参(default argument),当为一个形参赋予了默认值,则其后面的所有形参都必须有默认值
// 调用时,如果想覆盖某位置的默认实参,则必须为该位置之前的所有形参赋予实参
//一般在头文件中提供默认实参,并且在同一个作用域内,一个形参只能被赋值一次
//函数匹配(function matching)
//重载确定(overload resolution)
// 1.选定本次调用的重载函数集,候选函数(candidate function)
// 规则:1>同名 2.声明在调用点可用
// 2.考察实参,可行函数(viable function),如果找不到报告无匹配函数的错误
// 规则:1>形参数量和实参数量一致 2>实参的类型和对应形参的类型一致
// 3.最佳匹配
// 1>精确匹配
// 形参和实参类型相同
// 实参从数组类型或函数类型转换成对应的指针类型
// 顶层 cosnt
// 2.底层 const 转换
// 3.类型提升
// 4.算术类型转换
// 5.类类型转换
//三种调用结果:
//1>编译器找到一个与实参最佳匹配(best match)的函数,生产调用该函数的代码
//2>找不到与实参匹配(no match)的函数,编译器报无匹配的错误
//3>有多于一个函数可以匹配,但都不是最佳匹配,报二义性调用(ambiguous call)错误
//函数指针
//由返回类型和形参列表共同决定
//不同类型的函数指针不存在转换规则,但可以用 nullptr 或 0 的整型常量表达式赋值
//func1 的指针, 可以直接使用 pfunc1 调用函数
extern parr *(*pfunc1)(int);
//重载函数的指针,指针类型必须与重载函数的其中一个精确匹配
string &(*pShorter)(string &s1, string &s2); //未定义
#endif //FUNC_H
#include "func.h"
char &get_val(string &s, string::size_type pos)
{
//retrun 终止当前正在执行的函数并将控制权返回该函数调用的地方
//两种形式:
//return;
//只用于返回类型是 void 的函数中,也可以使用 returned expression 形式,但 expression 必须是一个返回 void 的函数
//returned expression
//只要函数的返回类型不是一个 void,则函数内的每条 return 语句必须返回一个值,该值的类型必须和函数的返回类型一致,或者能隐式转换
//函数完成后,所占用的存储空间也随之被释放掉,函数终止意味着局部变量的引用和指针不再指向有效的内存区域
return s[pos]; //pos 合法
}
int arr1[5]{1, 2, 3, 4, 5};
int arr2[5]{6, 7, 8, 9, 0};
//1.类型别名
parr *func1(int i)
{
return (i % 2) ? &arr1 : &arr2;
}
//2.不使用类型别名
int(*func2(int i))[5]
{
return (i % 2) ? &arr1 : &arr2;
}
//3.使用尾置返回类型 c++11
auto func3(int i) -> int(*)[5]
{
return (i % 2) ? &arr1 : &arr2;
}
//4.使用已知数组和 decltype 关键字
decltype(arr1) *func4(int i)
{
return (i % 2) ? &arr1 : &arr2;
}
//将 func4 改成返回引用
decltype(arr1) &func5(int i)
{
return (i % 2) ? arr1 : arr2;
}
//const_cast 强制类型转换
const string &shorter(const string &s1, const string &s2)
{
return s1.size() <= s2.size() ? s1 : s2;
}
//如果需要返回非常量的重载函数
string &shorter(string &s1, string &s2)
{
auto &r = shorter(const_cast<const string&>(s1), const_cast<const string&>(s2));
return const_cast<string&>(r);
}
//constexpr 函数,能用于常量表达式0
//所有的形参类型和返回类型均为字面值类型,函数体中有且只有一条 return 语句
//隐式定义为 inline
//允许返回值是一个非常量
constexpr int cexpr1(){ return 21;}
constexpr int cexpr2(const int n){ return cexpr1() * n;}
extern int arr3[cexpr2(2)];
int m = 1;
// extern int arr4[cexpr2(m)]; //cexpr2 返回非常量
parr *(*pfunc1)(int) = func1; //取地址符可选 parr *(*pfunc1)(int) = &func1
#include "func.h"
int main()//main函数不能重载
{
string s{"hello world"};
vector<int> vi;
get_val(s, 3) = 'M'; //调用返回引用的函数得到左值
cout <<"s : "<< s << endl;
vi = get_vector();
for (auto i : vi)
cout << i << endl;
parr *p1 = func1(2);
int (*p2)[5] = func2(3);
int (*p3)[5] = func3(5);
int (*p4)[5] = func4(2);
int (*p5)[5] = pfunc1(2); //直接使用 pfunc1 调用函数,解引用符可选(*pfunc1)(2)
for (size_t i = 0; i != 5; ++i)
{
cout << "P1: " << (*p1)[i] << endl;
cout << "P2: " << (*p2)[i] << endl;
cout << "P3: " << (*p3)[i] << endl;
cout << "P4: " << (*p4)[i] << endl;
cout << "P5: " << (*p5)[i] << endl;
}
func5(2)[3] = 6234; //返回引用类型可作为左值
for (size_t i = 0; i != 5; ++i)
{
cout << "arr2: " << arr2[i] << endl;
}
// const_cast 和 重载
string s1 = "hello";
string s2 = "world!";
// shorter("hello", "world!")[2] = 'H'; //调用const 形参版本,返回指向 const 对象的引用,不可以修改
shorter(s1, s2)[2] = 'H'; //调用非const 形参版本,返回非 const 引用,如果未定义非 const 形参版本,则返回 const 引用
cout << s1 << endl;
//允许 main 函数没有 return,编译器隐式插入一条返回 return 0 的语句
//0 表示执行成功,非 0 值具体含义由机器定义。cstdlib 定义了两个预处理变量 EXIT_FAILURE, EXIT_SUCCESS
return EXIT_SUCCESS;
}