C/C++教程

c++ 函数重载、内联函数、引用、类与对象、构造/析构函数

本文主要是介绍c++ 函数重载、内联函数、引用、类与对象、构造/析构函数,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

c++ 函数重载、内联函数、引用、类与对象、构造/析构函数

  • 函数重载
    • 什么是函数重载
    • C++是如何实现函数重载的
    • 参数的类型转换
    • 重载和隐藏
    • extern "C"
    • 指针的const属性会影响函数重载
  • 默认形参
    • 什么是默认形参
    • 默认形参的约束
  • 内联函数
    • 普通函数与内联函数
    • 内联函数的优缺点
    • 什么情况适合内联
    • 内联函数与宏函数的相同点与不同点
    • 显式内联和隐式内联
  • 引用
    • 什么是引用
    • 引用有哪些用处
    • 使用引用有哪些约束
    • 相比指针有哪些优点
  • 强制类型转换
  • 操作符别名
  • 面向对象和面向过程
  • 类和对象
  • 构造函数和析构函数

函数重载

什么是函数重载

    同一作用域、函数名相同,参数列表不同(参数的个数或类型不同)的函数构成重载关系。
    注意:函数的返回值与重载无关。

C++是如何实现函数重载的

    通过g++ -S xxx.cpp 生成 xxx.s文件可知
    编译器会把函数的参数类型缩写后追加到函数名的末尾,只要函数的参数列表不同,最终生成的函数名就不一样,该过程叫作换名。

参数的类型转换

    当调用函数时,会优先调用类型最精准的函数,如果没有再对实参进行类型提升,而不是直接出错。
    注意:当有多个备选者时可能会出错,调用重载过的函数时,参数要精准。

重载和隐藏

    子作域中的标识符会屏蔽上层作用域中的同名标识符,这种关系叫隐藏。
    不同作域下的同名函数,那怕参数列表同,也不会冲突。

extern “C”

C++代码在调用函数时会优先调用换名过函数,但最终的调用目标可以是C编译器编译出的目标文件或库文件,这样会造成连接失败。

解决方法:在声明函数 extern “C” { } 包一个函数的声明,这样C++编译器就会以C语言的规则就调用函数。

指针的const属性会影响函数重载

默认形参

什么是默认形参

    C++中的函数可以给参数设置一个默认值,当函数被调用时,如果调用者提供了实参则使用实参,如果没有提供实参则使用默认值。

默认形参的约束

    1、如果只有一部分形参设置了默认参数,则这些参数要全部靠右边。
    2、如果函数的声明和定义分开了,只需要在声明时设置默认参数即可。
    3、设置的默认参数必须是常量、常量表达式或全局变量。
    4、默认形参可能会引起重载函数的歧义。

内联函数

普通函数与内联函数

普通函数:会被编译器翻译成二进制指令,存储在代码段,当调用函数时,会在调用位置生成一句跳转指令,当执行到调用函数的代码时,会跳转到函数所在位置执行,执行完成后再返回。

内联函数:会被编译器翻译成二进制指令,存储在代码段,当调用内联函数时,编译器会从代码段中把内联函数的二进制指令拷贝到调用位置,当执行调用内存函数的代码时则直接执行不会发生跳转。

注意:内联是否生成由具体的编译器决定。

内联函数的优缺点

    优点:执行速度快
    缺点:最终的可执行文件会变大,代码段中有冗余。

什么情况适合内联

    代码量少,调用的位置少,执行的次数多

内联函数与宏函数的相同点与不同点

    相同点:都是用空间来换取时间(用代码增加的代价换取执行速度的提升)
    不同点:
        1、编译阶段拷贝、预处理阶段替换
        2、会检查参数类型(安全,有局限必),不检查参数(不安全,通用)
        3、有返回值,没有返回值(但可以有执行结果)
        4、不会产生二义性,可能产生二义性

显式内联和隐式内联

    显式内联:普通函数返回值前加 inline 关键字
    隐式内联:结构、联合、类的成员函数会自动内联 

引用

什么是引用

引用是一种取名机制。
int num = 10;
int& number = num;
number <=> num 

引用有哪些用处

    1、跨函数共享变量(获取函数的执行结果)
    2、提高传参数效率(不需要任何的内存拷贝)

使用引用有哪些约束

    1、引用必须初始化
    2、引用不更换目标
    3、当为了提高传参效率使用引用时,也需要 const。
    4、引用常量时必须加 const
        const int& num = 5;
    5、引用临时数据,但必须加 const
        const int& num = 3+3;      

相比指针有哪些优点

    1、安全
        不可能有空引用,不可能越界,极少有野引用(悬空),
        注意:返回局部变量的引用,就会产生野引用。
    2、效率高
        使用指针传参,至少需要拷贝4|8字节数据,而使用引用一字节也不用拷贝
5、指针与引用的相同点和不同点
    相同点:
        1、跨函数共享变量
        2、提高传参数效率
        3、都需要 const 保护
    不同点:
        身份:数据类型          取名机制
        字节数:4|8字节         0字节
        初始化:可以不初始化    必须初始化
        特殊值:空指针、野指针  没有空引用、极少有野引用
        目标:可以更换指向目标  不能更换引用目标
        堆内存:方便配合堆内存  不擅长引用堆内存
        可以定义指针型数组,不能定义引用型数组,但可以定义数组引用
        int (&arr)[10];
        可以定义多级指针,但不能定义多级引用。

强制类型转换

C++中重新设计一套类型转换规则,但C语言中的强制类型转换还可以继续使用。
static_cast<目标类型>(源类型对象) 静态类型转换
源类型的目标类型之间必须存在隐式转换关系,否则报错。

const_cast<目标类型>(源类型对象) 去常类型转换
源类型具有const属性的指针 目标类型没有const属性的指针,其它必须相同,否则报错。

reinterpret_cast<目标类型>(源类型对象) 重解释类型转换
源类型的目标类型必须是整型或指针类型

dynamic_cast<目标类型>(源类型对象) 动态类型转换
源类型和目标类型之间具有继承关系

操作符别名

{       <%
}       %>
[       <:
]       :>
#       %:
&&      and
||      or
!       not 
!=      not_eq
&       bitand
|       bitor
~       compl   

面向对象和面向过程

面向过程:关注的是如何解决问题,以及解决问题的步骤。
面向对象:
    抽象:先找出(想象)能解决问题的对象,分析该对象解决问题时需要的属性(成员变量)和行为(成员函数)。

    封装:把抽象的结果设计成一个类(结构),并且给属性和行为设置相应的访问权限(public/protected/private)。

    继承:
        1、在设计类的时候考虑现有的类是否能解决一部分问题,如果有则继承该类,然后在此基础上进行扩展达到解决问题的目的,以此缩短解决问题的时间。
        2、把一个复杂的问题拆分成若干个小问题,然后每个问题设计一个类去解决,最后使用继承把这些类型合并在一起达到解决问题的目的。

    多态:
        当发出一个指令时,系统会根据实际情况去执行相应的操作,这种特性就叫多态(指令的多种形态)。
        如:重载过的函数,编译器会根据调用时的实参类型决定最终调用哪个函数,由于该过程发生在编译时,因此也中编译时多态。
    
    注意:面向对象是从更宏观的解决去思考如何解决问题,这种方式解决问题的速度更快,更有利于团队合作,但具体到类的行为还是要以面向过程的方式去编程。

类和对象

什么是类和对象:
    一种由程序员设计的数据类型,相当于C语言中的结构。
    对象由类创建出的变量,相当于C语言中的结构变量,创建对象的过程也叫之前实例化。

类型的设计格式:
class 类名
{
private://可以省略
    成员变量;
public:
    成员函数;
};
注意:默认的成员默认权限是private

对象的实例化:
    类名 对象名;
    类名* 对象指针 = new 类名;

1、成员函数中可以直接访问成员变量。
2、一般会把成员变量设设置为private。
3、给需要外界使用的成员设计 getXxx、setXxx。

public:可以在任何位置访问
protected:可以在成员函数和子类中访问
private:只能在成员函数中访问

类的声明与实现:
在头文件声明类:
class 类名
{
    定义成员变量;
public:
    返回值 函数名(参数列表);
};
注意:如果类的代码量不多,可以直接在头文件全部实现。

在源文件实现类:
返回值 类名::函数名(参数列表);

构造函数和析构函数

构造函数:类的同名成员函数,当类对象实例化时会自动执行,一般负责对象的初始化和资源申请。
1、构造函数必须是public,否则无法实例化对象。
2、构造函数可以重载,可以有多个版本。
3、带参构造函数的调用:
    Test t(实参);
    Test* p = new Test(实参);
    Test t(); // 函数声明
4、默认情况下,编译器会自动给类生成无参构造函数,如果显式实现了构造函数,则编译器不再生成无参构造,再调用无参构造就会出错。
    Test t; // 调用无参构造
    Test* p = new Test; // 调用无参构造
    注意:一般实现了有参构造,需要再实现一个什么都干的无参构造。

析构函数:负责类对象销毁时自动执行的函数。
1、析构函数必须是public
2、没有参数,没有返回值
3、当对象的生命周期过期时或使用delete销毁对象时自动调用。
4、构造函数肯定执行,但析构函数不一定会执行。
5、如果没有显式实现析构函数,编译器会自动生成一个什么都做的析构函数。

注意:malloc和free不会自动调用构造函数析构函数。
这篇关于c++ 函数重载、内联函数、引用、类与对象、构造/析构函数的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!