同一作用域、函数名相同,参数列表不同(参数的个数或类型不同)的函数构成重载关系。 注意:函数的返回值与重载无关。
通过g++ -S xxx.cpp 生成 xxx.s文件可知 编译器会把函数的参数类型缩写后追加到函数名的末尾,只要函数的参数列表不同,最终生成的函数名就不一样,该过程叫作换名。
当调用函数时,会优先调用类型最精准的函数,如果没有再对实参进行类型提升,而不是直接出错。 注意:当有多个备选者时可能会出错,调用重载过的函数时,参数要精准。
子作域中的标识符会屏蔽上层作用域中的同名标识符,这种关系叫隐藏。 不同作域下的同名函数,那怕参数列表同,也不会冲突。
C++代码在调用函数时会优先调用换名过函数,但最终的调用目标可以是C编译器编译出的目标文件或库文件,这样会造成连接失败。
解决方法:在声明函数 extern “C” { } 包一个函数的声明,这样C++编译器就会以C语言的规则就调用函数。
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不会自动调用构造函数析构函数。