尽量使用常量引用做形参
(注意常量引用的 const 是底层 const。)- 可以用字面值初始化常量引用
使用常量引用的优点:
想要调用引用版本的reset,只能使用int 类型对象。而不能使用字面值、求值结果为int 的表达式、需要转换的对象或者const int 类型的对象。
- 想要调用指针班的reset 只能使用 int *
不能把 const 对象、字面值或需要类型转换的对象传递给普通的引用形参。但是可以传递给常量引用形参。
注意: 如果函数 a 把形参定义为了常量引用,函数 b 形参是普通引用,那么不能在 a 中使用 b 调用该常量引用形参。
bool is\_sentence(const string &s)
{
//如果在s的末尾有且只有一个句号,则s是一个句子
string :: size_type ctr = 0;
return find\_char(s, '.', ctr) == s.size() - 1 && ctr == 1;
}
错误因为is_sentence是const对象而find_char是普通对象
🎨6.2.4 数组形参
数组的两个特殊性质:不允许拷贝数组、使用数组时常会将其转换成指针
- 因为数组不能进行拷贝,所以无法使用值传递的方式使用数组参数。因为数组会被转换成指针,所实际上传递的是指向数组的首元素指针。
void print(const int\*);
void print(const int[]);
void print(const int[10]);//三种声明等价,数字 10 没有什么实际影响
int i= 0, j[2] = {0, 1};
printf(&i); //正确:&i的类型是int \*
printf(j); //正确:j转换成int \* 并指向j[0]
//传入的是一个数组,那么实参会自动转换成指向数组首元素的指针
使用数组做形参确保数组访问不越界的方法:
- 使用一个结束标记指定数组已结束,典型代表为 C 风格字符串
传递指向数组首元素和尾后元素的指针
显示传递一个表示数组大小的形参
数组引用形参
可以定义数组的引用,但是没有引用数组,因为引用不是一个对象。注意数组的大小是构成数组类型的一部分
func(int &arr[10]); //错误:arr 是引用的数组
func(int (&arr)[10]);//正确:array 是包含 10 个整数的整型数组的引用
int \*arr[10]; //指针的数组
int (\*arr)[10]; //指向数组的指针
🎨6.2.5 main:处理命令行选项
int main(int argc, char *argv[]){...}
- 第一个形参代表
数组中字符串的数量
;第二个形参argv是一个数组,它的元素是指向C风格字符串的指针
。
🎨6.2.6 含有可变形参的函数
无法预知应该向函数传递几个实参,处理不同数量实参的主要方法有俩种:
如果所有实参类型相同,传递一个 initializer_list 类型
使用省略符形参,它可以传递可变数量的实参,注意它一般仅用于与 C 函数交互的接口程序
initializer_list 形参特点
initializer_list
也是一种模板类型,定义在同名的头文件中。initializer_list
与 vector 容器大致相同,但是它的元素都是常量值
。initializer_list
对象只能使用花括号初始化
。- C++ 里的 vector 等各类容器使用列表初始化时本质上都是通过一个采用了 initializer_list 形参的构造函数进行初始化的。
操作 | 解释 |
---|---|
initializer_list<T> lst; | 默认初始化;T 类型元素的空列表 |
initializer_list<T> lst{a,b,c...}; | lst 的元素数量和初始值一样多;lst 的元素是对应初始值的副本;列表中的元素是const 。 |
lst2(lst) | 拷贝或赋值一个initializer_list 对象不会拷贝列表中的元素;拷贝后,原始列表和副本共享元素。 |
lst2 = lst | 同上 |
lst.size() | 列表中的元素数量 |
lst.begin() | 返回指向lst 中首元素的指针 |
lst.end() | 返回指向lst 中微元素下一位置的指针 |
void err\_msg(ErrCode e, initializer_list<string> il){
cout << e.msg << endl;
for (auto bed = il.begin(); beg != il.end(); ++ beg)
cout << \*beg << " ";
cout << endl;
}
err\_msg(ErrCode(404), {"functionX", "okay});
如果向 initailizer_list 形参中传递一个值的序列,必须把序列放在花括号里。
void func(initializer_list<int> il)
func({3,4,5,2});
省略符形参
- 省略符形参仅用于 C 和 C++ 通用的类型,大多数类类型的对象传递给省略符形参都无法正确拷贝。
- 省略符形参只能出现于形参列表的最后一个位置
void func(parm_list,...);
void func(...);
🎯6.3 返回类型和 return 语句
与参数传递一样,不能返回数组,只能返回指向数组的指针或数组的引用
return 俩个作用:
- 返回 return 语句中的值
- 终止当前正在执行的函数,将控制权返回到调用该函数的地方
🎨6.3.1 无返回值函数
- 没有返回值的 return语句只能用在返回类型是 void的函数中
返回 void的函数不要求非得有 return语句。因为它会在最后一句后面隐式地执行 return。
🎨6.3.2 有返回值函数
- return语句的返回值的类型
必须和函数的返回类型相同
,或者能够隐式地转换成函数的返回类型
。 在含有 return 语句的循环和条件后面也应该有一条 return 语句
。- 返回一个值和初始化一个变量或形参的方式一样,返回的值用于初始化调用点的变量。
不要返回局部对象的引用和指针
因为局部对象在函数调用完成后,他所占用的存储空间被释放,此时函数终止意味着局部变量的引用将指向不再有效的内存区域
const string& func()
{
string s;
s = "xiaodainiao"
return s; //错误,字符串字面值转换成一个局部临时变量,不能返回局部对象的引用
}
bool str\_cmp(const string &str1, const string &str2)
{
for (int i = 1; i < n; i ++)
{
if (str[1] != str[2])
return; //错误
}
return true; //这个往往会被忽略,在含有 return 语句的循环和条件后面也应该有一条 return 语句
}
引用返回左值
返回引用的函数返回的是左值,其他返回类型得到右值
。可以为返回类型是非常量引用的函数的结果赋值
。
char &get\_val(string &str, string::size_type ix)
{
return str[ix];
}
get\_val(s,0) = 'A';
列表初始化返回值
vector<string> process()
{
string s;
if(expected.empty())
return {};// 返回一个空 vector 对象
else if(expected == actual)
return {"funcitonX","okay"};//返回一个列表初始化的 vector 对象
}
main 的返回值
- 如果结尾没有return,编译器将
隐式地插入一条返回0的return语句
。 - 返回0代表执行成功,返回其它值代表失败。
cstdlib
头文件定义了两个预处理变量来表示成功与失败
return EXIT_FAILURE;//失败
return EXIT_SUCCESS;//成功
🎨6.3.3 返回数组指针
- 函数不能返回数组,但是可以返回数组的指针或引用
int \*p[10]; // 错误,指针的数组,p是一个含有10个指针的数组
int (\*p)[10]; // 正确,数组的指针,p是一个指针,指向含有10个整数的数组
int &p[10]; //错误,引用数组,引用不是对象,没有引用的数组,有数组的引用
int (&p) [10]; // 正确,数组引用
int(*function(int i )[10]);
func(int i )
表示调用func函数需要传递一个int 实参(*func(int i ))
对函数调用的结果执行解引用(*function(int i )[10])
表示解引用结果是一个大小为10的数组int(*function(int i )[10])
表示数组中的元素是int 类型
有三种方法简化返回数组指针或引用。
使用类型别名
使用尾置返回类型
使用 decltype
使用类型别名
typedef int arrT[10]; // arrT 表示含有 10 个整数的数组
using arrT = int[10]; //等价声明
arrT\* func(int i); // 函数 func 返回一个指向含有 10 个整数的数组的指针
使用尾置返回类型
- 任何函数的定义都可以使用尾置返回,适用于返回类型
复杂的函数
auto func(int i) -> int(\*)[10];
//func接受一个int 类型的实参,返回一个指针,该指针指向含有10个整数的数组
使用 decltype
- 如果已知函数返回的指针将指向哪个数组,可以使用 decltype
int odd[]={1,3,5,7,9};
decltype(odd)\* func(int i);//返回一个数组指针
注意 decltype 的结果是一个数组,要想表示func返回指针还必须在函数声明时加一个*
号。
🎯6.4 函数的重载
- main 函数不能重载
- 函数
名字相同
,但是形参数量
和形参类型
不同,此时发生重载。 - 函数
返回值类型不同
,不是函数重载
重载和const形参
- 函数重载
无法区分顶层 const 形参和非顶层 const 形参
,但是可以区分底层const 形参与普通形参
int func(int i);
int func(const int i);//顶层const,无法区分,相当于重复声明
int func(int\* p);
int func(int\* const p);//顶层const,无法区分,相当于重复声明
int func(int\* p);
int func(const int\* p);//底层const,可以区分,一个普通指针,一个常量指针
int func(int& i);
int func(const int& i);//底层const,可以区分,一个普通引用,一个常量引用
const_cast
- 强制类型转化,可以将常量引用转换为非常量引用,也可以将非常量引用转换为常量引用
- 当要重载常量引用与非常量引用的版本时,
在非常量引用的版本中可以通过 const_cast 将参数和返回值从常量引用转换为非常量引用,以实现对常量引用版本的调用。
string& s;
const_cast <const string&> (s);// 将 s 转换为常量引用
const_cast <string&> (s);// 将 s 转换回非常量引用
const string &str(const string &s1, const string &s2)
{
return s1.size() > s2.size();
}
string &str( string &s1, string &s2)
{
auto &r = str(const_cast<const string&> (s1), const_cast<const string&> (s2))
return const_cast<string&> (r);
}
- 将实参转换为const的引用,然后调用str的const版本。
- 当返回时,因为返回类型是非常量引用,所以需要再次转换一下。
6.4.1 重载与作用域
- 不同的重载版本要定义在同一作用域中(一般都是全局)
🎯6.5 特殊用途语言特性
🎨6.5.1 默认实参
- 一旦某个形参被赋予了默认值,那么它之后的形参都必须要有默认值。(反过来就是设置默认值的形参必须都放在没有默认值的形参后面。)
string screen(sz ht = 24, sz wid = 80, char backgrnd = ' ');
如果想使用默认值实参时,只要在调用该函数的时候省略该实参就可以(默认实参负责填补函数调用时缺少的尾部实参。)
局部变量不能作为默认实参,全局变量和字面值都可以。
- 如果函数有默认实参,则调用函数时传入的实参数量可能少于它实际使用的实参数量
通常应该在函数声明中指定默认实参,并将该声明放在合适的头文件中
。
string screen(sz, sz, char = ' ');
string screen(sz, sz ,char = '\*'); //错误重复声明,不能修改已经存在的默认值
🎨6.5.2 内联函数和constexpr函数
内联函数可避免函数调用时的开销
- 内联函数适合于规模小、流程直接、频繁调用的函数。
使用函数的缺点
调用函数更慢
,调用前要先保存寄存器(保护现场),并在返回时恢复:可能需要拷贝实参;程序转向一个新的位置继续执行。
在函数前用 inline 声明一下即表明是内联函数
🎨constexpr函数
只能用于常量表达式的函数。
但是 constexpr 函数不一定返回常量表达式。- constexpr int new_sz() {return 42;}
constexpr 函数的返回类型及所有的形参类型都必须是字面值类型,函数体中必须有且只有一条 return 语句
- constexpr函数应该在头文件中定义。
- 应该把内联函数和constexpr函数的定义放到头文件里。
🎯6.6 函数匹配
- 重载函数匹配的三个步骤:
1.候选函数;2.可行函数;3.寻找最佳匹配。
- 候选函数:选定本次调用对应的重载函数集,集合中的函数称为候选函数(candidate function)。
- 可行函数:考察本次调用提供的实参,选出可以被这组实参调用的函数,新选出的函数称为可行函数
- 寻找最佳匹配:基本思想:实参类型和形参类型越接近,它们匹配地越好。
调用重载函数应尽量避免强制类型转换。
🎨6.6.1 实参类型转换
实参类型到形参类型的转换分为几个等级,具体排序如下:
- 精确匹配
实参类型和形参类型相同
从数组类型或函数类型转化为对应的指针类型
向实参中添加顶层const或删除顶层const - 通过const转换实现的匹配。
- 通过类型提升实现的匹配
- 通过算术类型转换或指针转换实现的匹配
- 通过类类型转换实现的匹配
注意:所有算数类型转换的级别都一样。
🎯6.7 函数指针
- 函数指针指向的是函数而非对象
一种函数指针只能指向一种特定的函数类型:
bool Compare(const string&, const string&); // 此函数的类型是 bool(const string&, const string&);
bool (\*pf)(const string&, const string&); // 声明了一个指向 bool(const string&, const string&) 类型函数的指针,注意括号不能少;
pf = Compare; // 给指针赋值,指向 Compare 函数
pf = &Compare; // 这两种赋值语句完全等价
bool b1 = pf("hello","goodbye"); // 可以直接使用指针替代函数名调用函数。
bool b2 = (\*pf)("hello","goodbye"); // 与上面的等价
bool b3 = Compare("hello","goodbye") //与上面等价
当把函数名作为一个值使用时,函数自动地转换成指针
。- 不同函数类型的指针间不能相互转换。函数指针也可以指向
nullptr 或 0
。 - 对于重载函数,指针类型必须与重载函数中的某一个精确比配。
string::size_type str(cosnt string&,const strint&)
bool str1(const char\*, const char\*)
pf = 0;
pf = str; //错误,返回类型不匹配
pf = str1; //错误,形参类型不匹配
pf = Compare; //正确,函数和指针类型精确匹配
函数指针形参
函数不能作形参,但是函数指针可以做形参,之后在调用时可以直接传入函数名作实参
函数名做形参也会自动的转换为指针。
类似于数组不能做形参,但是数组指针可以做形参
bool GetBigger(const string& s1, const string& s2, bool(\*comp)(const string&, const string&)); // 函数指针做形参
GetBigger(s1, s2, Compare); // 实参直接传入函数名 Compare
返回函数指针
不能返回一个函数,但是可以返回函数指针(注意这时函数名不会自动转换为函数指针)
。
类似于返回数组,数组不能返回,只能返回指向数组的指针
- 声明一个返回函数指针的函数有几种方法,其中直接声明最麻烦,使用尾置类型和 decltype 更简单一些,但是最好使用类型别名。
'直接声明'
bool (\*f1(int))(double); // f1 是一个函数,函数的形参列表为 int,返回类型为函数指针。这个函数指针的类型为 bool (\*)(double),即形参为 double,返回类型为 bool。
'使用尾置类型'
auto f1(int) -> bool(\*)(double);
'使用 decltype'
bool func(double);
decltype(func)\* f1(double);
'使用类型别名'
using PF = bool(\*)(double);
PF f1(int);
和数组的类似,可以进行对比
typedef int arrT[10]; // arrT 表示含有 10 个整数的数组
using arrT = int[10]; //等价声明
arrT\* func(int i); // 函数 func 返回一个指向含有 10 个整数的数组的指针
auto func(int i) -> int(\*)[10];
//func接受一个int 类型的实参,返回一个指针,该指针指向含有10个整数的数组
int odd[]={1,3,5,7,9};
decltype(odd)\* func(int i);//返回一个数组指针
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
整数的数组
using arrT = int[10]; //等价声明
arrT* func(int i); // 函数 func 返回一个指向含有 10 个整数的数组的指针
auto func(int i) -> int(*)[10];
//func接受一个int 类型的实参,返回一个指针,该指针指向含有10个整数的数组
int odd[]={1,3,5,7,9};
decltype(odd)* func(int i);//返回一个数组指针
[外链图片转存中...(img-zixV9Q9d-1714785127877)]
[外链图片转存中...(img-JIoHxSgG-1714785127878)]
[外链图片转存中...(img-5g7RtXKB-1714785127878)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**
**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**