文章目录
函数重载
函数重载匹配
函数重载的原理
代码示例
函数的缺省参数(默认实参)
代码示例
哑元函数
代码示例
内联函数(inline)
C++的动态内存分配
代码示例
引用(reference)
定义
常引用
引用型函数参数
代码示例
函数重载
在相同的作用域,定义同名的函数,但是它们的参数有所区分,这样的函数之间的关系称为函数重载。
函数重载匹配
调用重载关系的函数时,编译器将根据实参和形参匹配程度,自动选择最优的重载版本。
当前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; }
- 执行结果