C++——函数

简介: C++——函数

文章目录

函数重载

函数重载匹配

函数重载的原理

代码示例

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

代码示例

哑元函数

代码示例

内联函数(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;
}


  • 执行结果

20200210092926310.png

  • 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;
}


  • 执行结果

20200210093740880.png

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

  1. 可以为函数的部分或者全部参数指定缺省值,调用该函数时,如果不给传实参,可以用缺省值作为相应的实参值。
void func(int a,int b,int flag=0){}

  1. 靠右原则,如果函数的一个参数有缺省值,那么这个参数右侧的所有参数都必须带有缺省值。
void func(int a = 0,int b){}//error
  void func(int b,int a = 0){}//ok

  1. 如果函数的定义和声明分开,缺省参数应该写在函数的声明部分,而定义部分不写。
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;
}


  • 执行结果

20200210094017569.png

哑元函数

  1. 只有类型而没有变量名的形参成为哑元
void func(int/*哑元*/){...}

  1. 使用哑元的场景
  1. 操作符重载中,区分前后++/- -
  • 为了兼容旧代码

代码示例

  • 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;
}


  • 执行结果

20200210094700221.png

内联函数(inline)

  1. 使用inline关键字修饰的函数,表示这个函数是内联函数,编译器将常识做内联优化,避免函数的调用开销,提高程序的执行效率。
  2. 说明:
  1. 多次调用的小而简单的函数适合内联。
  2. 调用次数极少或大而复杂的函数不适合内联。
  3. 递归函数不能内联优化。
  • 注:内联只是一种建议而不是强制要求,一个函数能否内联优化主要取决于编译器,有些函数不加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;
}


  • 执行结果

20200210095855568.png

引用(reference)

定义

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


常引用

  1. 定义引用是加 const 修饰,即为常引用,不能通过常引用修改引用的目标。
int a = 10;
  const int& b = a;//b就是a的常引用
  //int const& b = a;//和上面等价

  1. 普通的引用也可以称为左值引用,只能引用左值;而常引用也可以称为万能引用,既可以引用左值又可以引用右值。
  2. 关于左值和右值
左值(lvalue):可以放在赋值操作符左侧,可以被修改
-->非const普通变量都是左值
右值(rvalue):只能放在赋值操作符右侧,不能被修改
-->常量
-->临时变量
  int a = 100;
  //a(int)-->tmp(double)
  double& r = a;//error 
  int& r = 100;//error


引用型函数参数

  1. 将引用用于函数参数,这时形参就是实参的别名,可以通过形参直接修改实参变量的值,同时还能避免参数值的传递过程,提高代码执行效率。
  2. 引用参数可能意外修改实参的值,如果不希望修改实参变量本身,可以将形参定义为常引用,提高传参效率的同时还可以接受常量型实参。

代码示例

  • 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
}


  • 执行结果

20200210104314546.png

  • 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;
}


  • 执行结果

20200210104656322.png

  • 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;
}


  • 执行结果

20200210105221878.png

  • 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;
}


  • 执行结果

20200210110404731.png

相关文章
|
2月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
2月前
|
存储 前端开发 C++
C++ 多线程之带返回值的线程处理函数
这篇文章介绍了在C++中使用`async`函数、`packaged_task`和`promise`三种方法来创建带返回值的线程处理函数。
79 6
|
2月前
|
C++
C++ 多线程之线程管理函数
这篇文章介绍了C++中多线程编程的几个关键函数,包括获取线程ID的`get_id()`,延时函数`sleep_for()`,线程让步函数`yield()`,以及阻塞线程直到指定时间的`sleep_until()`。
37 0
C++ 多线程之线程管理函数
|
2月前
|
编译器 C语言 C++
C++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
38 3
|
2月前
|
编译器 C语言 C++
详解C/C++动态内存函数(malloc、free、calloc、realloc)
详解C/C++动态内存函数(malloc、free、calloc、realloc)
347 1
|
2月前
|
存储 编译器 C++
C++入门3——类与对象2-1(类的6个默认成员函数)
C++入门3——类与对象2-1(类的6个默认成员函数)
48 1
|
2月前
|
安全 编译器 C++
【C++篇】C++类与对象深度解析(三):类的默认成员函数详解
【C++篇】C++类与对象深度解析(三):类的默认成员函数详解
24 3
|
2月前
|
编译器 C语言 C++
C++入门6——模板(泛型编程、函数模板、类模板)
C++入门6——模板(泛型编程、函数模板、类模板)
60 0
C++入门6——模板(泛型编程、函数模板、类模板)
|
2月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(二)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作
|
3月前
|
编译器 C++
【C++核心】函数的应用和提高详解
这篇文章详细讲解了C++函数的定义、调用、值传递、常见样式、声明、分文件编写以及函数提高的内容,包括函数默认参数、占位参数、重载等高级用法。
29 3