C++知识点总结(基础语法1-函数重载, 默认参数)

文章目录

    • 函数重载1
    • 函数重载2
    • 默认参数
    • extern "C" 1-作用
    • extern "C" 2-C,C++混合开发
    • extern "C" 3-__cplusplus
    • extern "C" 4-#pragma once

  1. 默认情况下, 不管是double, 还是float , 输出一个小数, 最多输出6位有效数字.
  2. string 字符串类型 包含在#include 头文件中
  3. 布尔类型 bool 作用: 布尔数据类型代表真或假的值
    bool类型只有两个值:true 真(本质是1)
    false 假(本质是0)
    bool类型占1个字节大小
    另外, bool类型除了0代表假, 其它都代表真.(非0的值都代表真)
    例如:如果输入给bool的值为100, 那么flag的值为1
bool flag = true;
cout << flag << endl;
flag = false;
cout << flag << endl;
// 输出结果为1
//0
  1. 两个整数相除, 结果仍然是整数, 将小数部分直接舍弃掉.
  2. 两个小数相除, 如果可以整除, 结果是整数, 如果不可以整除, 结果是小数.(cout 的智能)
double a = 0.5;
double b = 0.25;
cout << a / b << endl;
// 结果是整数2
double a = 0.5;
double b = 0.22;
cout << a / b << endl;
//结果是小数
  1. C++ 不允许两个小数做取模运算(实际也很少见)
  2. string 数组
string names[3] = {"张三", "李四", "王五"};
cout << names[0] << endl;
//打印张三

函数重载1

(1)两个函数函数名相同, 但是参数的个数或者参数的类型不同.返回值不同, 不能构成函数重载.因为

double fun() {
	return 0;
}
int fun() {
	return 0;
}
int main() {
	fun();// 有歧义, 不知道调用哪个函数.
}

(2)调用函数时, 实参的隐式类型转换可能会产生二义性.

#include 
using namespace std;

void display(long a) {
    cout << "long" << a << endl;
}

void display(double a) {
    cout << "double" << a << endl;
}

int main()
{
    display(10);// int类型
    display(10L);// long类型
    display(10.0);// double类型
    return 0;
}
// 此时有问题, 因为int类型的10, 可以隐式类型转换为long和double类型, 产生二义性.

函数重载2

本质:采用了name mangling或者叫name decoration技术
函数重载的底层是编译器采取了name mangling技术, 就是C++编译器默认会根据参数对符号名(比如函数名)进行修饰,改编, 生成新的函数名, 这个新的函数名会包含参数的信息.所以其实调用时函数名其实是不同的可以通过IDA不用debug模式(会添加很多调试信息导致看不到),而release模式(不添加调试信息,但会被编译器做优化也会导致看不到)所以采用release模式禁止优化来看到, 所以这些函数名可以同时存在, 并且可以正常调用.
即:重载时会生成多个不同的函数名, 不同的编译器(MSVC(微软VS的编译器), g++)有不同的生成规则.

默认参数

C++允许函数设置默认参数, 在调用时可以根据情况省略实参, 规则如下:
(1)默认参数只能按照右到左的顺序.

#include 
using namespace std;

int func(int v1, int v2 = 3, int v3 = 5) {
    return v1 + v2 + v3; 
}

int func1(int v1 = 3, int v2 = 3, int v3 = 4) {
    return v1 + v2 + v3; 
}

/*
int func2(int v1 = 3, int v2) {

} 
错误,默认参数必须从右到左*/

int main()
{
    cout << func(1) << endl;
    cout << func(1,2) << endl;
    cout << func(1, 2, 3) << endl;
    cout << func1() << endl;
    return 0;
}

(2)如果函数同时有声明, 实现, 默认参数只能放在函数声明中.
(3)默认参数的值可以是常量, 全局符号(全局变量, 函数名)
例如:

#include 
using namespace std;

int age = 20;

void sum(int v1 = 5, int v2 = age) {
    cout << v1 + v2 << endl; 
}

int main()
{
    sum();
    return 0;
}

#include 
using namespace std;

void func(int v1, void(*p)(int)) {
    p(v1);
}

void test(int a) {
    cout << a << endl;
}

int main()
{
    func(20, test);

    return 0;
}

#include 
using  namespace std;

void test(int a) {
    cout << a << endl;
}

void func(int v1, void(*p)(int) = test) { // 默认参数是函数名
    p(v1);
}

int main()
{
    func(20);
    return 0;
}

(4)用途: 如果函数的实参经常是同一个值, 可以考虑使用默认参数
(5)函数重载, 默认参数可能会产生冲突, 二义性(建议优先选择默认参数)

#include 
using namespace std;

void display(int a, int b = 20) {
    cout << a << endl;
}

void display(int a) {
    cout << a << endl;
}

int main()
{
    display(10);
    // 10既可以传给第一个也可以传给第二个, 产生二义性
    return 0;
}

(6)默认参数的本质:
还是传两个参数, 只不过传的是默认参数的值, 和原来的没有不同, 只不过编译器帮我们做了一些事情而已.

extern “C” 1-作用

(1)被extern “C” 修饰的代码会按照C语言的方式去编译
例如:

#include 
using namespace std;

extern "C" void func() {

}

// 会产生错误
extern "C" void func(int v) {

}
int main()
{
    
    return 0;
}


也可以

#include 
using namespace std;

extern "C" {
    void func() {

    }
    void func(int v) {

    }
}

int main()
{
    return 0;
}

(2)如果函数同时有声明和实现, 要让函数声明被extern “C” 修饰, 函数实现可以不修饰.

extern “C” 2-C,C++混合开发

(1)第三方框架\库:可能是用C语言写的开源库
在C++里调用C语言函数时, 经常用到extern “C”
因为C语言和C++的编译规则不同, C语言没有name mangling技术来修饰函数名.

extern "C" {
	#include "math.h"// 因为math.h是用C语言写的第三方库, 所以要加extern "C", 这样可以把第三方库的函数声明全拿过来, 在源文件里就可以调用函数了.
}

但如果把extern “C” 直接写在第三方库的头文件里更方便, 在别人用第三方库时, 不用extern "C"了.但是这样会导致另一个问题, 如果用在C语言代码里时, C语言没有extern "C"这个东西, 所以会报错, 所以要用下面的东西

extern “C” 3-__cplusplus

C++环境下, 编译器会自动定义一个宏, __cplusplus
代表这是C++环境, 所以可以通过是否有这个宏来区分C++环境.例如:在math.h头文件中

#ifdef __cplusplus
extern "C" {
#endif

int sum(int v1, int v2);
int del(int v1, int v2);
int divide(int v1, int v2);

#ifdef __cplusplus
}
#endif

(2)补充:在自己编写的第三方库.c文件中, 一般也要包含自己写的.h头文件, 因为这个.c文件可能会存在函数之间互相调用, 而没有头文件, 就不能调用了.
(3)补充:在.h文件中, 为了防止重复包含头文件,在.h文件中这样写:

#ifndef ABC  //举例
#define ABC
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus

int sum(int v1, int v2);
int del(int v1, int v2);
int divide(int v1, int v2);

#ifdef __cplusplus
}
#endif   // __cplusplus

#endif  // !ABC

这样如果出现重复包含头文件时, 会因为已经定义过ABC导致后面的代码不会参与编译.防止头文件的内容被重复包含.
但是如果还有一个.h文件也瞎写成ABC, 就会导致错误
所以这个宏名是有规范的, 规定: 写成文件名就可以保证不同例如: __MATH_H
#ifndef __MATH_H
#define __MATH_H
因为这是特殊的宏所以要带下划线.

extern “C” 4-#pragma once

#pragma once 可以防止整个头文件的内容被重复包含
与#ifndef #define #endif 差不多
区别:
(1)较老的编译器不支持比如:gcc 3.4版本之前
(2)#ifndef #define #endif 受C\C++标准的支持, 不受编译器的任何限制.
(3)#ifndef #define #endif 可以针对一个文件中的部分代码, 而#pragma once 只能针对整个文件.


其他C++系列文章:

C++知识点总结(基础语法1-函数重载, 默认参数)
C++知识点总结(基础语法2-内联函数, const, 引用)
C++知识点总结(面向对象1-类和对象, this指针, 内存布局)
C++知识点总结(面向对象2-构造函数, 初始化列表)

C++知识点总结(面向对象3-多态)

C++知识点总结(面向对象4-多继承, 静态成员static)
C++知识点总结(面向对象5-const成员, 拷贝构造函数)
C++知识点总结(面向对象6-隐式构造, 友元, 内部类, 局部类)
C++知识点总结(其他语法1-运算符重载)
C++知识点总结(其他语法2-模板, 类型转换, C++11新特性)

你可能感兴趣的:(C++)