文章目录
函数重载
在相同的作用域,定义同名的函数,但是它们的参数有所区分,这样的函数之间的关系称为函数重载。
函数重载匹配
调用重载关系的函数时,编译器将根据实参和形参匹配程度,自动选择最优的重载版本。
当前g++编译器匹配的一般规则:
完全匹配 >=常量转换 > 升级转换 > 降级转换 > 省略号
函数重载的原理
C++编译器是通过对函数进行换命,将参数表的类型信息整合到新的名字中,解决函数重载和名字冲突的矛盾。
使用关键字“extern”用于声明C++中的函数,要求该函数不做换名以方便C程序调用该函数。
代码示例
- overload.cpp
#include <iostream>
using namespace std;
int foo(int i){
cout << "foo(int)" << endl;
}
void foo(int i,int j){
cout << "foo(int,int)" << endl;
}
void foo(int a,float f){
cout << "foo(int,float)" << endl;
}
int main(void)
{
foo(10);
foo(10,20);
foo(10,1.23f);
/*函数指针的类型决定其匹配的版本*/
void (*pfoo)(int,float) = foo;
pfoo(10,20);
return 0;
}
- 执行结果
- 02overload.cpp
#include <iostream>
using namespace std;
//char->int:升级转换
void bar(int i){
cout << "bar(1)" << endl;
}
//char->const char:*常量转换*
void bar(const char c){
cout << "bar(2)" << endl;
}
//short->char:降级转换
void func(char c){
cout << "func(1)" << endl;
}
//short->int:升级转换
void func(int i){
cout << "func(2)" << endl;
}
//省略号匹配(最差)
void hum(int i,...){
cout << "hum(1)" << endl;
}
//double->int:降级转换
void hum(int i,int j){
cout << "hum(2)" << endl;
}
int main(void)
{
char c = 'a';
bar(c);
short s = 10;
func(s);
hum(10,1.23);
return 0;
}
- 执行结果
函数的缺省参数(默认实参)
- 可以为函数的部分或者全部参数指定缺省值,调用该函数时,如果不给传实参,可以用缺省值作为相应的实参值。
void func(int a,int b,int flag=0){}
- 靠右原则,如果函数的一个参数有缺省值,那么这个参数右侧的所有参数都必须带有缺省值。
void func(int a = 0,int b){}//error
void func(int b,int a = 0){}//ok
- 如果函数的定义和声明分开,缺省参数应该写在函数的声明部分,而定义部分不写。
void func(int a = 缺省值);//函数声明
void func(int a){...};//函数定义
代码示例
- defarg.cpp
#include <iostream>
using namespace std;
//函数声明
void foo(int a,int b = 200,int c = 300);
int main(void)
{
foo(10);//10,200,300
foo(10,20);//10,20,300
foo(10,20,30);//10,20,30
return 0;
}
//函数定义
void foo(int a,int b/*=200*/,int c/*=300*/){
cout << a << ',' << b << ',' << c
<< endl;
}
- 执行结果
哑元函数
- 只有类型而没有变量名的形参成为哑元
void func(int/*哑元*/){...}
- 使用哑元的场景
- 操作符重载中,区分前后++/- -
- 为了兼容旧代码
代码示例
- 02defarg.cpp
#include <iostream>
using namespace std;
//函数声明
void foo(int a,int=0/*哑元*/);
int main(void)
{
foo(10);//10,200,300
foo(10,20);//10,20,300
foo(10,20,30);//10,20,30
return 0;
}
//函数定义
void foo(int a,int=0){
cout << "a = " << a << endl;
}
- 执行结果
内联函数(inline)
- 使用inline关键字修饰的函数,表示这个函数是内联函数,编译器将常识做内联优化,避免函数的调用开销,提高程序的执行效率。
- 说明:
- 多次调用的小而简单的函数适合内联。
- 调用次数极少或大而复杂的函数不适合内联。
- 递归函数不能内联优化。
- 注:内联只是一种建议而不是强制要求,一个函数能否内联优化主要取决于编译器,有些函数不加inline修饰,编译器也会默认处理为内联优化;有些函数即便加了inline的修饰也会被编译器忽略。
C++的动态内存分配
之前C语言中动态内存分配使用:
- 分配(malloc())
- 释放(free())
- 错误处理(根据返回值进行错误处理)
而现在C++中使用操作符实现动态内存管理
- 分配:new、new[]
- 释放:delete、delete[]
- 错误处理:C++异常机制
代码示例
- new.cpp
#include <iostream>
using namespace std;
int main(void)
{
int* pi = new int;
*pi = 123;
cout << *pi << endl;
delete pi;//防止内存泄露
pi = NULL;//避免野指针
//new内存同时初始化
int* p2 = new int(321);
cout << *p2 << endl;//321
delete p2;
p2 = NULL;
//new数组,包含10元素
int* pa = new int[10];
for(int i=0;i<10;i++){
pa[i] = i;
cout << pa[i] << ' ';
}
cout << endl;
delete[] pa;
pa = NULL;
//new数组,同时初始化,需要C++11标准支持
int* pa2 =
new int[10]{9,8,7,6,5,4,3,2,1,0};
for(int i=0;i<10;i++){
cout << pa2[i] << ' ';
}
cout << endl;
delete[] pa2;
pa2 = NULL;
return 0;
}
- 执行结果
引用(reference)
定义
- 引用就是某个变量的别名,对引用的操作与对该变量本身完全相同。
- 语法
类型 & 引用名 = 变量名;
注:引用在定义时必须要初始化,初始化以后绑定的目标不能再修改。
注:引用的类型和它绑定的目标变量类型要一致
eg:
int a = 10;
int& b = a;//b就是a的别名
b++;
cout << a << endl;//11
int c = 20;
b = c;//赋值操作,不是改变引用的目标
cout << a << endl;//20
常引用
- 定义引用是加 const 修饰,即为常引用,不能通过常引用修改引用的目标。
int a = 10;
const int& b = a;//b就是a的常引用
//int const& b = a;//和上面等价
- 普通的引用也可以称为左值引用,只能引用左值;而常引用也可以称为万能引用,既可以引用左值又可以引用右值。
- 关于左值和右值
左值(lvalue):可以放在赋值操作符左侧,可以被修改
-->非const普通变量都是左值
右值(rvalue):只能放在赋值操作符右侧,不能被修改
-->常量
-->临时变量
int a = 100;
//a(int)-->tmp(double)
double& r = a;//error
int& r = 100;//error
引用型函数参数
- 将引用用于函数参数,这时形参就是实参的别名,可以通过形参直接修改实参变量的值,同时还能避免参数值的传递过程,提高代码执行效率。
- 引用参数可能意外修改实参的值,如果不希望修改实参变量本身,可以将形参定义为常引用,提高传参效率的同时还可以接受常量型实参。
代码示例
- reference.cpp
#include <iostream>
using namespace std;
int main(void)
{
int a = 66;
int& b = a;//b引用a,b就是a的别名
cout << "&a=" << &a << endl;
cout << "&b=" << &b << endl;
b++;
cout << "a=" << a << endl;//67
cout << "b=" << b << endl;//67
//int& r;//error
//double& r = a;//error
}
- 执行结果
- constref.cpp
#include <iostream>
using namespace std;
int func(void){
int num = 123;
//编译会生成临时变量保存return数据
return num;//int tmp=num
}
int main(void)
{
//函数返回的是临时变量(右值),普通引用
//无法接收,常引用可以
//int res = tmp;
//int& res = func();//error
const int& res = func();//ok
cout << res << endl;//123
//100是常数(右值),普通引用不能引用右值
//int& ri = 100;//error
//常引用可以引用右值
const int& ri = 100;//ok
int i = 200;
//i是左值,但是给double类型rd初始化时需要
//先做隐式转换,转换之后结果会保存到一个
//临时变量中,临时变量都是右值,所以error
//double& rd = i;//error
//rd实际引用的是临时变量,不是i本身
const double& rd = i;
cout << "&i=" << &i << endl;
cout << "&rd=" << &rd << endl;
return 0;
}
- 执行结果
- refArg.cpp
#include <iostream>
using namespace std;
/*void swap1(int* x,int* y){
*x = *x ^ *y;//011 ^ 101 = 110(x)
*y = *x ^ *y;//110 ^ 101 = 011(y)->3
*x = *x ^ *y;//110 ^ 011 = 101(x)->5
}*/
void swap2(int& x,int& y){
x = x ^ y;
y = x ^ y;
x = x ^ y;
}
int main(void)
{
int a = 3,b = 5;
//swap1(&a,&b);
swap2(a,b);
//a=5,b=3
cout << "a=" << a << ",b=" << b << endl;
return 0;
}
-
执行结果
-
02refArg.cpp
#include <iostream>
using namespace std;
struct Student{
char name[128];
int age;
int no;
};
void print(const Student& s){
cout << "我叫" << s.name << ",今年" <<
s.age/*++*/ <<"岁,学号是" << s.no
<< endl;
}
int main(void)
{
const Student stu = {"老王",45,10086};
print(stu);
print(stu);
print(stu);
return 0;
}
- 执行结果