• C++入门知识


    目录

    1. C++关键字(C++98)

    2. 命名空间

    2.1 命名空间定义

    2.2 命名空间使用

    3.C++输入输出

    4.缺省函数

    缺省参数分类

    5. 函数重载

    5.1 函数重载概念

    5.2 C++支持函数重载的原理

    5.3 extern “C”


    1. C++关键字(C++98)

    在讨论有多少关键字的时候,要说清楚在那个版本之下,在C++98中,C++ 总计 63 个关键字, C 语言 32 个关键字,这里只是一个展示表,在后面的学习中慢慢掌握

    C语言和C++源文件后缀是不同的

    C++源文件的后缀是 .cpp。后缀不同工程不同,背后的使用的编译器是不一样的。

    2. 命名空间

    C/C++ 中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。
        使用命名空间的目的是对标识符的名称进行本地化 ,以 避免命名冲突或名字 污染 namespace 关键字的出现就是针对这种问题的
    1. #include
    2. #include
    3. int rand = 10;
    4. int main()
    5. {
    6. printf("%d\n", rand);
    7. return 0;
    8. }
    C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决
    编译后后报错: error C2365: “rand”: 重定义;以前的定义是 函数

    2.1 命名空间定义

    定义命名空间,需要使用到 namespace 关键字 ,后面跟 命名空间的名字 ,然 后接一对 {} 即可, {} 中即为命名空间的成员。
    下面是最基本的命名空间定义
    1. #include
    2. //之前在全局作用域中可以定义的内容,在命名空间内都可以定义
    3. namespace zx
    4. {
    5. //定义变量
    6. int a = 10;
    7. int b = 20;
    8. //定义函数
    9. int Add(int x, int y)
    10. {
    11. return x + y;
    12. }
    13. //定义结构体
    14. struct BTNode
    15. {
    16. struct BTNode* left;
    17. struct BTNode* right;
    18. int data;
    19. };
    20. }
    21. int a = 10;
    22. int b = 20;
    23. int Add(int x, int y)
    24. {
    25. return x + y;
    26. }
    27. int main()
    28. {
    29. return 0;
    30. }

    2.命名空间嵌套:一个命名空间中,可以包含其他的命名空间

    类似于:学校可以看作一个命名空间,学校下包含各个学院,学院下又包含不同专业

    1. namespace zx
    2. {
    3. int a = 120;
    4. int b = 20;
    5. int Add(int x, int y)
    6. {
    7. return x + y;
    8. }
    9. namespace zx2
    10. {
    11. int x = 10;
    12. int y = 20;
    13. }
    14. }

    3.同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。

    ps :一个工程中的 test.h 和上面 test.cpp 中两个 N1 会被合并成一个

    2.2 命名空间使用

    1.加命名空间名称及作用域限定符
    1. int a = 10;
    2. int main()
    3. {
    4. int a = 30;
    5. printf("%d\n", a);
    6. return 0;
    7. }

    如上,直接打印出来a为30,要打印全局作用域中的a,就要使用作用域运算符

    ::作用域运算符

    printf("%d\n", ::a);

    这样编译器就会找全局作用域中的a ,找到了就打印,找不到会报错

    以此类推,你要找上面 定义的 命名空间中的 a,加上名字即可
    printf("%d\n", zx::a);

    这样打出来就是120

    2. 使用 using 将命名空间中某个成员引入
    使用using,相当于把b变成了当前文件中的全局变量,下面打印出来的结果相同的,但是如果你在文件全局作用域中再次定义一个b。就会报错
    1. using N::b;
    2. int main()
    3. {
    4. printf("%d\n", N::a);
    5. printf("%d\n", b);
    6. return 0;
    7. }

    3.使用using namespace 命名空间名称 引入

    把命名空间N中所有成员都定义为当前文件中的全局变量;

    但是一般不推荐使用,容易冲突

    1. using namespce N;
    2. int main()
    3. {
    4. printf("%d\n", N::a);
    5. printf("%d\n", b);
    6. Add(10, 20);
    7. return 0;
    8. }

    命名空间嵌套时,内部命名空间中成员的使用方式

    如下,打印出来结果为x的值

    1. namespace zx
    2. {
    3. int a = 110;
    4. int b = 20;
    5. namespace zx2
    6. {
    7. int x = 10;
    8. int y = 20;
    9. }
    10. }
    11. int main()
    12. {
    13. printf("%d\n", zx::zx2::x);
    14. return 0;
    15. }

    3.C++输入输出

    cin输入,cout输出

    cin在接受输入时,不能接受空格 tab 回车,如要接受,使用getchar

    1. 使用 cout 标准输出对象 ( 控制台 ) cin 标准输入对象 ( 键盘 ) 时, 必须包含< iostream >头文件 以及按命名空间使用方法使用std
    2. cout cin 是全局的流对象, endl 是特殊的 C++ 符号,表示换行输出,他们都包含在包含 < iostream > 头文件中。
    3. << 是流插入运算符, >> 是流提取运算符
    4. 使用 C++ 输入输出更方便,不需要像 printf/scanf 输入输出时那样,需要手动控制格式。 C++ 的输入输出可以自动识别变量类型。
    5. 实际上 cout cin 分别是 ostream istream 类型的对象, >> << 也涉及运算符重载等知识,这些知识我们我们后续才会学习,所以我们这里只是简单学习他们的使用。后面我们还有有一个章节更深入的学习IO流用法及原理。
    写项目时推荐如下:
    1. #include
    2. int main()
    3. {
    4. std::cout << "12" << std::endl;
    5. return 0;
    6. }

    练习时可使用:

    1. #include
    2. using namespace std;
    3. int main()
    4. {
    5. cout << "12" << endl;
    6. return 0;
    7. }
    std C++ 标准库的命名空间,如何展开 std 使用更合理呢?
    1. 在日常练习中,建议直接 using namespace std 即可,这样就很方便。
    2. using namespace std 展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型 / 对象 / 函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模大,就很容易出现。所以建议在项目开发中使用,像std::cout 这样使用时指定命名空间 + using std::cout 展开常用的库对象 / 类型等方式

    4.缺省函数

    声明或定义函数时,可以给函数的参数带上默认值,在调用函数时,如果用户没有指定实参,则使用默认值,如果用户指定了实参,则使用用户传递的实参
    1. void Func(int a = 0)
    2. {
    3. cout<
    4. }
    5. int main()
    6. {
    7. Func(); // 没有传参时,使用参数的默认值
    8. Func(10); // 传参时,使用指定的实参
    9. return 0;
    10. }

    缺省参数分类

    1.全缺省参数:所有的参数都有默认值
    1. #include
    2. using namespace std;
    3. void fun(int a = 1, int b = 2, int c = 3)
    4. {
    5. cout << "a = " << a << endl;
    6. cout << "b = " << b << endl;
    7. cout << "c = " << c << endl;
    8. cout << "------" << endl;
    9. }
    10. int main()
    11. {
    12. fun();
    13. fun(10);
    14. fun(10, 20);
    15. fun(10, 20, 30);
    16. return 0;
    17. }

    大家对比结果可看出规律,当传入不全时,依次传参 

    2.半缺省参数
    部分参数带有默认值
    a和b有默认值,c没有--》编译报错
    a和c有默认值,b没有--》编译报错
    b和c有默认值,a没有--》编译成功
    注意事项:
    1. 半缺省参数必须 右往左依次来给出 ,不能间隔着给
    2. 缺省参数不能在函数声明和定义中同时出现
            假设声明和定义的位置可以同时给出,如果两个地方的默认值不一样,编译器就不能判断使用那个值。
            一般情况下,如果缺省值放在函数定义的位置,用户就不知道函数的形参是否带默认值。所以 一般放在函数声明的地方
    3. 缺省值必须是常量或者全局变量
    4. C语言不支持(编译器不支持)

    5. 函数重载

    5.1 函数重载概念

    函数重载: 是函数的一种特殊情况, C++ 允许在 同一作用域中声明几个功能类似的同名函数 ,这些同名函数的形参列表 ( 参数个数 或 类型 或 类型顺序 ) 不同 ,常用来处理实现功能类似数据类型不同的问题
    1. int Add(int left, int right)
    2. {
    3. return left + right;
    4. }
    5. double Add(double left, double right)
    6. {
    7. return left + right;
    8. }
    9. int main()
    10. {
    11. cout << Add(1, 2) << endl;
    12. cout << Add(1.2, 2.3) << endl;
    13. return 0;
    14. }

    5.2 C++支持函数重载的原理

    C/C++ 中,一个程序要运行起来,需要经历以下几个阶段: 预处理、编译、汇编、链接
    在上面程序中,编译阶段对形参类型进行推演:编译器确认实参的类型
    上面首先 1,2都是int,两个形参都是int,最终就会调用 int 的ADD函数
    后面double类型的同理
    如果如此输入
    cout << Add(1, 2.3) << endl;
    此时,如果没有参数类型对应的函数,编译器会对实参进行隐式类型转换,如果转换之后有类型匹配的函数则调用,否则报错,如下,会把double型转换为int型,输出为都是3
    1. int Add(int left, int right)
    2. {
    3. return left + right;
    4. }
    5. int main()
    6. {
    7. cout << Add(1, 2) << endl;
    8. cout << Add(1.2, 2.3) << endl;
    9. return 0;
    10. }

    一些注意的点:
    1. 实际项目通常是由多个头文件和多个源文件构成,而通过 C 语言阶段学习的编译链接,我们可以知道, 【当前a.cpp 中调用了 b.cpp 中定义的 Add 函数时】,编译后链接前, a.o 的目标文件中没有 Add 的函数地 址,因为Add 是在 b.cpp 中定义的
    c语言编译器仅仅在函数名字前添加了下划线

     所以,下面两个函数在c语言中的名字最终被修饰为: _Add

    虽然两个方法的原型不同,编译器认为这两函数为同一函数,导致重定义出错。

    1. int Add(int left, int right)
    2. {
    3. return left + right;
    4. }
    5. double Add(int left, int right)
    6. {
    7. return left + right;
    8. }
    上面同样的代码,在C++中运行无报错
    2. 所以链接阶段就是专门处理这种问题, 链接器看到 a.o 调用 Add ,但是没有 Add 的地址,就会到 b.o 的符 号表中找 Add 的地址,然后链接到一起 ( 老师要带同学们回顾一下 )
    3. 链接时, 面对 Add 函数,这里每个编译器都有自己的函数名修饰规则。
    4. 由于 Windows vs 的修饰规则过于复杂,而 Linux g++ 的修饰规则简单易懂,下面我们使用了 g++ 演示了这个修饰后的名字。
    5. 通过下面我们可以看出 gcc 的函数修饰后名字不变。而 g++ 的函数修饰后变成【 _Z+ 函数长度 + 函数名 + 型首字母】。
    6. C 语言没办法支持重载,因为同名函数没办法区分。而 C++ 是通过函数修饰规则来区 分,只要参数不同,修饰出来的名字就不一样,就支持了重载

    7. 如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分

    5.3 extern “C”

    由于 C C++ 编译器对函数名字修饰规则的不同,在有些场景下可能就会出问题,比如:
    1. C++ 中调用 C 语言实现的静态库或者动态库,反之亦然
    2. 多人协同开发时,有些人擅长用 C 语言,有些人擅长用 C++
    在这种混合模式下开发,由于 C C++ 编译器对函数名字修饰规则不同,可能就会导致链接失败,在该种场景下,就需要使用extern "C" 在函数前加 extern "C" ,意思是告诉编译器,将该函数按照 C 语言规则来编译
    具体创建规则可以在网上查找
  • 相关阅读:
    微信小程序-云开发 起步
    【设计模式】【结构型7-1】【代理模式】【动态代理】【静态代理】
    【小程序】九宫格抽奖,页面不是有点丑,功能没啥问题,有需要直接拿去改吧
    图像几何变换
    实时数据传输:Django 与 MQTT 的完美结合
    如何使用蓝牙实现OTA固件升级
    架构设计-跨域问题的根源及解决方式
    PMP考试提分必刷题
    gradle-2初始化篇(LoadSetings)
    (四)在docker中安装nginx
  • 原文地址:https://blog.csdn.net/weixin_59215611/article/details/127617901