C++ 笔记2 | 重载与缺省参数 内联 动态内存分配 引用

八、 C++的函数

1、函数重载(overload)

1)定义
在相同作用域,可以定义同名的函数,但是参数必须有所区分,这样函数构成重载关系.
注:函数重载和返回类型无关。

eg:实现图形库中一些绘图函数

//C语言
	void drawRect(int x,int y,int w,int h){}
	void drawCircle(int x,int y,int r){}
	...
	-----------------
//C++语言
	void draw(int x,int y,int w,int h){}
	void draw(int x,int y,int r){}
	...

举例:overload1.cpp

#include 
using namespace std;
int func(int i){
    cout << "func(int)" << endl;
}
void func(int a,int b){
    cout << "func(int,int)" << endl;
}
void func(int i,float f){
    cout << "func(int,float)" << endl;
}
int main(void){
    func(10);//调用func(int i)
    func(10,20);//调用func(int a,int b)
    //func(10,3.14);//歧义错误,因为3.14是double类型
    func(10,3.14f);//func(int,float)
    
    //由函数指针类型决定匹配的重载版本
    void (*pfunc)(int,float) = func;
    pfunc(10,20);

    return 0;
}

2)函数重载匹配
调用重载关系的函数时,编译器将根据实参和形参的匹配程度,自动选择最优的重载版本,当前g++编译器匹配一般规则:
完全匹配>=常量转换>升级转换>降级转换>省略号
举例overload2.cpp

#include 
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 hum(char c){
    cout << "hum(1)" << endl;
}
//short->int:升级转换
void hum(int i){
    cout << "hum(2)" << endl;
}
//省略号匹配
void fun(int i,...){
    cout << "fun(1)" << endl;
}
//double->int:降级转换
void fun(int i,int j){
    cout << "fun(2)" << endl;
}
int main(void){
    char c = 'a';
    bar(c);//优先考虑常量转换调用bar(const char c)
    short s = 100;
    hum(s);//优先考虑升级转换调用hum(int i)
    fun(100,3.14);//优先考虑降级转换fun(int i,int j)

    return 0;
}

3)函数重载原理
C++的编译器在编译函数时,会进行换名,将参数表的类型信息整合到新的函数名中,因为重载关系的函数参数表有所区分,换出的新的函数名也一定有所区分,解决了函数重载和名字冲突的矛盾。

笔试题:C++中extern "C"作用?
	在C++函数声明时加extern "C",要求C++编译器不对该函数进行换名,便于C程序直接调
	用该函数.
	注:extern "C"的函数无法重载。

2、 函数的缺省参数(默认实参)

1)可以为函数参数指定缺省值,调用该函数时,如果不给实参,就取缺省值作为默认实参。
void func(int i,int j=0/缺省参数/){}
2)靠右原则:如果函数的某个参数带有缺省值,那么该参数右侧的所有参数都必须带有缺省值。
3)如果函数的声明和定义分开写,缺省参数应该写在函数的声明部分,而定义部分不写。
举例:defArg.cpp

#include 
using namespace std;
//函数声明
void func(int a,int b = 20,int c = 30);
//void func(int i){}//注意歧义错误
int main(void){
    func(11,22,33);
    func(11,22);//11 22 30
    func(11);//11 20 30
    return 0;
}
//函数定义
void func(int a,int b/*=20*/,int c/*=30*/){
    cout << "a=" << a << ",b=" << b << ",c="
        << c << endl;
}

3、函数的哑元参数

1)定义函数时,只有类型而没有变量名的形参被称为哑元
void func(int){…}
2)需要使用哑元场景
–》在操作符重载函数中,区分前后++、-- //后面讲
–》兼容旧的代码

算法库:void math_func(int a,int b){...}
使用者:
	int main(void){
		...
		math_func(10,20);
		...
		math_func(10,20);
		...
	}
-----------------------------------------
升级算法库:void math_func(int a,int/*哑元*/){...}
使用者:
	int main(void){
		...
		math_func(10,20);
		...
		math_func(10,20);
		...
	}

4、内联函数(inline)

1)使用inilne关键字修饰的函数,即为内联函数,编译器将会尝试进行内联优化,可以避免函数调用开销,提高代码执行效率.
inline void func(void){…}
2)使用说明
–》多次调用小而简单的函数适合内联优化
–》调用次数极少或大而复杂的函数不适合内联
–》递归函数不能内联优化
–》虚函数不能内联优化//后面讲

注:内联只是一种建议而不是强制的语法要求,一个函数能否内联优化主要取决于编译器,有些函数不加inline修饰也会默认处理为内联优化,有些函数即便加了inline修饰也会被编译器忽略。

九、C++动态内存管理

1、回顾C语言动态内存管理

1)分配:malloc()
2)释放:free()

2、C++动态内存管理

1)分配:new/new[]
2)释放:delete/delete[]
举例:new.cpp

#include 
using namespace std;
int main(void){
    //动态分配内存保存1个int数据
    //int* pi = (int*)malloc(4);//C
    int* pi = new int;//C++
    *pi = 123;
    cout << *pi << endl;
    //free(pi);
    delete pi;//防止内存泄露
    pi = NULL;//避免野指针

    //动态分配内存同时初始化
    int* pi2 = new int(200);
    cout << *pi2 << endl;//200
    (*pi2)++;
    cout << *pi2 << endl;//201
    delete pi2;
    pi2 = NULL;

    //动态分配内存保存10个int
    //int* parr = new int[10];
    
    //new数组同时初始化,需要C++11支持
    int* parr = 
        new int[10]{1,2,3,4,5,6,7,8,9,10};
    for(int i=0;i<10;i++){
        //*(parr+i) = i+1
        //parr[i] = i+1;
        cout << parr[i] << ' ';
    }
    cout << endl;
    delete[] parr;
    parr = NULL;

    return 0;
}

举例:delete.cpp

#include 
using namespace std;

int main(void){
    int* p1;
    //delete p1;//delete野指针,危险!

    int* p2 = NULL;
    delete p2;//delete空指针,安全,但是无意义

    int* p3 = new int;
    delete p3;
    delete p3;//不能多次delete同一个地址

    return 0;
}

十、C++引用(Reference)

1、定义

1)引用即别名,引用就是某个变量别名,对引用操作和对变量本身完全相同.
2)语法
类型 & 引用名 = 变量名;
注:引用必须在定义同时初始化,而且在初始化以后所绑定的目标变量不能再做修改.
注:引用类型和绑定目标变量类型要一致。
eg:
int a = 10;
int & b = a;//b就是a的别名
b++;
cout << a << endl;//11
a++;
cout << b << endl;//12

int c = 123;
b = c;//仅是赋值
cout << a << endl;//123

举例:reference.cpp

#include 
using namespace std;
int main(void){
    int a = 10;
    int& b = a;//b引用a,b就是a的别名
    cout << "a=" << a << ",b=" << b << endl;
    cout << "&a=" << &a << ",&b=" << &b<<endl;
    b++;
    cout << "a=" << a << ",b=" << b << endl;
    
    //int& r;//error,引用定义时必须初始化
    int c = 20;
    b = c;//ok,但不是修改引用目标,仅是赋值

    cout << "a=" << a << ",b=" << b << endl;
    cout << "&a=" << &a << ",&b=" << &b<<endl;

    //char& rc = c;//error,引用类型和目标要一致

    return 0;
}

2、常引用

1)定义引用时可以加const修饰,即为常引用,不能通过常引用修改目标变量.
const 类型 & 引用名 = 变量名;
类型 const & 引用名 = 变量名;//和上面等价

int a = 10;
const int& b = a;//b就是a常引用
cout << b << endl;//10
b++;//error

举例:constRef.cpp

#include 
using namespace std;
int main(void){
    //int& r1 = 100;//error
    const int& r1 = 100;//ok
    cout << r1 << endl;//100

    int a=10,b=20;
    //int& r2 = a+b;//error
    //r2引用的是a+b表达式结果的临时变量(右值)
    const int& r2 = a+b;
    cout << r2 << endl;//30
    return 0;
}

2)普通引用也可以称为左值引用,只能引用左值;而常引用也可以称为万能引用,既可以引用左值也可以引用右值。

3)关于左值和右值
左值(lvalue):可以放在赋值表达式左侧,可以被修改
右值(rvalue):只能放在赋值表达式右侧,不能被修改

练习:测试下面表达式结果,是左值还是右值?
int a,b;
a+b;//右值
a+=b;
++a;
a++;

练习:总结关于引用和指针区别和联系(手机百度)

你可能感兴趣的:(C++,c++,重载,内联,动态内存分配)