• C++入门


    目录

    一、命名空间

     1.1 命名冲突域

    1.2 命名空间的内容

    ​1.3 命名空间可以嵌套

     1.4 同一个工程允许多个相同名称的命名空间,编译器最后会将其合成同一个命名空间​

    1.5  命名空间的使用

     1.5.1 加命名空间名称及作用域限定符

    1.5.2 使用using namespace命名空间名称引入

    1.5.3  使用using 将命名空间中某个成员引入

    1.5.4  三种命名空间的使用示例

    二、C++输入和输出

    2.1 cout和cin的运用​编辑

    2.2 C语言和C++语言的输出比较

    三、 缺省参数

    3.1 全缺省参数

    3.2 半缺省参数​编辑

    四、 函数重载

    4.1 参数类型不同

    4.2 参数个数不同

    4.3 参数顺序不同

    4.4  是否构成重载的几种情况

    五、引用

     5.1 引用特性

    5.1.1 引用在定义时必须初始化及一个变量可以有多个引用

     5.1.2 引用一旦引用一个实体,再不能引用其他实体

    5.2 传指针、传引用、传值

     5.3 传引用作参数的应用

     5.4 传引用作返回值

    5.5 常引用

     5.6 const在引用中的应用

    5.7 引用和指针的不同点

    六、extern "C"

    6.1  将c++项目运用C实现的StaticLib1静态库

     6.1.1 步骤

     6.2 C工程调用C++库步骤

     七、 内联

    7.1 特性

     八、auto关键字(C++11)

     8.1 auto的使用

    8.1.1  auto与指针和引用结合起来使用

    ​8.1.2 在同一行定义多个变量

     8.2 auto 不能推导的场景

    8.2.1.auto不能作为函数的参数

    8.2.2 auto  不能直接用来声明数组

    九、基于范围的for循环(c++11)

    9.1 范围for的语法

    9.2 范围for 的使用条件

     十、 指针空值nullptr(C++)


    一、命名空间

    C/C++ 中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是 对标识符的名称进行本地化 ,以 避免命名冲突或名字 污染 namespace 关键字的出现就是针对这种问题的。

    定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名 空间的成员。

     1.1 命名冲突域

    1.2 命名空间的内容

    1.3 命名空间可以嵌套

     1.4 同一个工程允许多个相同名称的命名空间,编译器最后会将其合成同一个命名空间

    注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中

    1.5  命名空间的使用

     1.5.1 加命名空间名称及作用域限定符

    1.5.2 使用using namespace命名空间名称引入

    全部展开,用起来方便了,隔离就失效了,所以应该谨慎使用

    1.5.3  使用using 将命名空间中某个成员引入

    1. //单独展开某一项,用于展开命名空间中常用的
    2. using N1::N2::Node;
    3. int main()
    4. {
    5. struct Node node;
    6. }

    1.5.4  三种命名空间的使用示例

    二、C++输入和输出

    2.1 cout和cin的运用

    说明:
    1. 使用 cout 标准输出 ( 控制台 ) cin 标准输入 ( 键盘 ) 时,必须 包含 < iostream > 头文件 以及 std 标准命名空间。
    注意:早期标准库将所有功能在全局域中实现,声明在 .h 后缀的头文件中,使用时只需包含对应头文件即可,后来将其实现在 std 命名空间下,为了和 C 头文件区分,也为了正确使用命名空间,规定 C++ 头文 件不带 .h ;旧编译器 (vc 6.0) 中还支持 <iostream.h> 格式,后续编译器已不支持,因此 推荐 使用 <iostream>+std 的方式。
    2. 使用 C++ 输入输出更方便,不需增加数据格式控制,比如:整形 --%d ,字符 --%c

    2.2 C语言和C++语言的输出比较

    上图可以看出,如果只输出数值的话,还是C++更方便一些,C语言保留了6位小数。而如果结构体student中的成员越多的话,还是C语言的输出语句更方便一些,所以C语言和C++哪个更方便就可以选用哪个。

    三、 缺省参数

    缺省参数是 声明或定义函数时 为函数的 参数指定一个默认值 。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。

    3.1 全缺省参数

     

    3.2 半缺省参数

    注意:
    1. 半缺省参数必须 从右往左依次 来给出,不能间隔着给
    2. 缺省参数不能在函数声明和定义中同时出现
    1. //a.h
    2. void TestFunc(int a = 10);
    3. // a.c
    4. void TestFunc(int a = 20)
    5. {}
    6. // 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那
    7. 个缺省值。
    3. 缺省值必须是常量或者全局变量
    4. C 语言不支持(编译器不支持)

    四、 函数重载

    函数重载 : 是函数的一种特殊情况, C++ 允许在 同一作用域中 声明几个功能类似 的同名函数 ,这些同名函数的形参列表 ( 参数个数 或 类型 或 顺序 ) 必须不同 ,常用来处理实现功能类似数据类型不同的问题

    4.1 参数类型不同

    4.2 参数个数不同

     

    4.3 参数顺序不同

    4.4  是否构成重载的几种情况

     

     

     

    五、引用

    引用 不是新定义一个变量,而 是给已存在变量取了一个别名 ,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
    比如:
    李逵 ,在家称为 " 铁牛 " ,江湖上人称 " 黑旋风

     类型& 引用变量名(对象名) = 引用实体

    引用在语法层上来讲是没有开辟新的空间的,就是对原来的空间取了一个新名称叫做b
    注意:引用类型必须和引用实体同种类型

     5.1 引用特性

    5.1.1 引用在定义时必须初始化及一个变量可以有多个引用

     5.1.2 引用一旦引用一个实体,再不能引用其他实体

    5.2 传指针、传引用、传值

    1. //传指针
    2. void Swap(int* p1,int* p2)
    3. {
    4. int temp = *p1;
    5. *p1 = *p2;
    6. *p2 = temp;
    7. }
    8. //传引用,引用做参数,把a,b起个别名
    9. void Swap(int& r1,int& r2)
    10. {
    11. int temp = r1;
    12. r1 = r2;
    13. r2 = temp;
    14. }
    15. //传值
    16. void Swap(int r1, int r2)
    17. {
    18. int temp = r1;
    19. r1 = r2;
    20. r2 = temp;
    21. }
    22. //他们三个构成重载(类型不同)
    23. //但是Swap(a,b);调用是存在歧义,他们不知道调用,传值还是传引用
    24. int main()
    25. {
    26. int a = 10, b = 20;
    27. Swap(&a,&b);
    28. /*Swap(a,b);*/
    29. }

     5.3 传引用作参数的应用

     5.4 传引用作返回值

    注意: 如果函数返回时,出了函数作用域,如果返回对象还未还给系统(全局变量、静态区),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回,不能使用传引用返回.

    5.5 常引用

    1. //常引用
    2. int main()
    3. {
    4. //权限放大: a是只读的,而b是a的引用,竟然是可读可写的,那是不可以的
    5. const int a = 10;
    6. int& b = a;
    7. //权限不变: c和d都是只读的 ,可以
    8. const int c = 10;
    9. const int& d = c;
    10. //权限缩小:f是只读的,可以
    11. int e = 10;
    12. const int& f = e;
    13. }

     5.6 const在引用中的应用

    5.7 引用和指针的不同点

    1. 引用 在定义时 必须初始化 ,指针没有要求
    2. 引用 在初始化时引用一个实体后,就 不能再引用其他实体 ,而指针可以在任何时候指向任何一个同类型实体
    3. 没有 NULL 引用 ,但有 NULL 指针
    4. sizeof 中含义不同 引用 结果为 引用类型的大小 ,但 指针 始终是 地址空间所占字节个数 (32 位平台下占
    4 个字节 )
    5. 引用自加即引用的实体增加 1 ,指针自加即指针向后偏移一个类型的大小
    6. 有多级指针,但是没有多级引用
    7. 访问实体方式不同, 指针需要显式解引用,引用编译器自己处理
    8. 引用比指针使用起来相对更安全
    9.引用概念上定义一个变量的别名(没开辟空间),指针存储一个变量的地址

    六、extern "C"

    有时候在 C++ 工程中可能需要 将某些函数按照 C 的风格来编译 在函数前加 extern "C",意思是告诉编译器, 将该函数按照 C 语言规则来编译。

    6.1  将c++项目运用C实现的StaticLib1静态库

    1.包含头文件
    2.在工程属性中配置静态库的目录和添加静态库
    注意:可以理解为,只有c++认识extern C

     6.1.1 步骤

     6.2 C工程调用C++库步骤

    总结:
    C++程序调用C的库,在C++程序中加extern"C"
    C程序调用C++的库,在C++库中加extern"C"

     七、 内联

    因为调用函数,需要建立栈帧,栈帧中要保存一些寄存器,结束后又要恢复,频繁的调用函数,这样的操作是有消耗的。而C和C++都有应对的措施。
    C语言通过宏(一种替换,不需要建立栈帧)

    1. #define Add(x,y) ((x)+(y))
    2. //最好把((x)+(y))中x和y括号都加上,否则比如(x+y),10*Add(3,4),会替换为10 * 3+4,会导致出错
    3. int main()
    4. {
    5. cout << Add(1, 2) << endl;
    6. return 0;
    7. }
    C++ 以 inline 修饰 的函数叫做内联函数, 编译时 C++ 编译器会在 调用内联函数的地方展开 ,没有函数压栈的开销, 内联函数提升程序运行的效率。

    查看方式:

    1. release 模式下,查看编译器生成的汇编代码中是否存在 call Add
    2. debug 模式下,需要对编译器进行设置,否则不会展开 ( 因为 debug 模式下,编译器默认不会对代码进行优化,以下给出 vs2013 的设置方式 )

    7.1 特性

    1. inline 是一种 以空间换时间 的做法,省去调用函数额开销。所以 代码很长 或者有 循环 / 递归 的函数不适宜使用作为内联函数。
    2. inline 对于编译器而言只是一个建议 ,编译器会自动优化,如果定义为 inline 的函数体内有循环 / 递归等 等,编译器优化时会忽略掉内联。
    3. inline 不建议声明和定义分离(直接再定义前面加inline),分离会导致链接错误。因为 inline 被展开,就没有函数地址了,链接就会找不到。

     八、auto关键字(C++11)

    在早期 C/C++ auto 的含义是:使用 auto 修饰的变量,是具有自动存储器的局部变量 ,但遗憾的是一直没有人去使用它,大家可思考下为什么?
    C++11 中,标准委员会赋予了 auto 全新的含义即: auto 不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器, auto 声明的变量必须由编译器在编译时期推导而得
    1. int main()
    2. {
    3. const int a = 0;
    4. int b = 0;
    5. //自动将a的类型推到c,
    6. auto f = a;//int
    7. auto c = &a;//int const*
    8. auto d = 'A';//char
    9. auto e = 10.11;//double
    10. //f,d,e的类型是const的,具有常性,但是auto会忽略这个const
    11. //typeid打印变量的类型
    12. cout << typeid(f).name() << endl;
    13. cout << typeid(c).name() << endl;
    14. cout << typeid(d).name() << endl;
    15. cout << typeid(e).name() << endl;
    16. auto e;//err,auto定义变量时必须对其进行初始化
    17. }
    [注意]
    使用 auto 定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导 auto 的实际类型 。因此 auto 并非是一种 类型 的声明,而是一个类型声明时的 占位符 ,编译器在编译期会将 auto 替换为 变量实际的类型

     8.1 auto的使用

    8.1.1  auto与指针和引用结合起来使用

    auto 声明指针类型时,用 auto auto* 没有任何区别,但用 auto声明引用类型时则必须加&
     

    8.1.2 在同一行定义多个变量

    当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量
    1. void TestAuto()
    2. {
    3. auto a = 1, b = 2;
    4. auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
    5. }

     8.2 auto 不能推导的场景

    8.2.1.auto不能作为函数的参数

    8.2.2 auto  不能直接用来声明数组

    九、基于范围的for循环(c++11)

    9.1 范围for的语法

    在C/C++中遍历数组可以有如下的方式

    1. //语法糖
    2. int array[] = {1,2,3,4,5};
    3. //c/c++遍历数组
    4. for (int i = 0; i < sizeof(array)/sizeof(int);i++)
    5. {
    6. cout << array[i] << endl;
    7. }
    对于一个 有范围的集合 而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此 C++11 中引入了基于范围的 for 循环。 for 循环后的括号由冒号 分为两部分:第一部分是范围内用于迭代的变量, 第二部分则表示被迭代的范围
    1. //C++11 范围for
    2. //自动依次取数组array中的每个元素赋值给e
    3. for (auto e:array)
    4. {
    5. cout << e << endl;
    6. }
    注意:与普通循环类似,可以用 continue 来结束本次循环,也可以用 break 来跳出整个循环

     那如何运用范围for将数组中的每个值+1呢?

    1. for (auto e : array)
    2. {
    3. e++;//此时是array数组将每个值赋值给e,e的改变并不会影响到array数组中的值
    4. }

    这个方法是不行的,因为此时是array数组将每个值赋值给e,e的改变并不会影响到array数组中的值,那怎样才能改变呢?可以用引用的方法,数组中的每个值赋值给e变量,而e变量又是该值的别名,修改的是同一份空间,所以会改变数组的值,而应该注意的是每次的e的赋值的作用域只在一个循环之中。这种方法是可以解决这个问题的,代码如下:

    1. for (auto& e : array)
    2. {
    3. e++;//此时是array数组将每个值赋值给e,e的改变并不会影响到array数组中的值
    4. }

    9.2 范围for 的使用条件

    for 循环迭代的范围必须是确定的 :
    对于数组而言,就是数组中第一个元素和最后一个元素的范围 ;对于类而言,应该提供 begin end 的方法, begin end 就是 for 循环迭代的范围。
    1. void TestFor(int array[])
    2. {
    3. for (auto& e : array)
    4. cout << e << endl;
    5. }
    6. int main()
    7. {
    8. int array[] = { 1,2,3,4,5 };
    9. TestFor(array);
    10. }

     以上的代码是有问题的,for的范围是不确定的,因为范围for的范围必须是数组名,而数组传参,传过去之后会退化为指针,所以其实TestFor的array是一个指针,所以是有问题的,应当注意

     十、 指针空值nullptr(C++)

    NULL其实是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

    1. #ifndef NULL
    2. #ifdef __cplusplus
    3. #define NULL 0
    4. #else
    5. #define NULL ((void *)0)
    6. #endif
    7. #endif

    此时NULL可能被定义为常量,或者被定义为无类型指针(void*)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦。问题如下:

    1. void f(int)
    2. {
    3. cout << "f(int)" << endl;
    4. }
    5. void f(int*)
    6. {
    7. cout << "f(int*)" << endl;
    8. }
    9. int main()
    10. {
    11. f(0);
    12. f(NULL);
    13. f((int*)NULL);
    14. return 0;
    15. }

     

     f(NULL)和f(0)对应的都是f(int),而程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的初衷相悖。而在C++98中,字面常量0既可以是一个整形数字,也可以是无类型的指针(void*)常量,但是编译器默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0, 所以在C++11中,引入了新关键字nullptr。

    注意:

    1. 在使用 nullptr 表示指针空值时,不需要包含头文件,因为 nullptr C++11 作为新关键字引入的
    2. C++11 中, sizeof(nullptr) sizeof((void*)0) 所占的字节数相同。
    3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用 nullptr
  • 相关阅读:
    ASP.NET Core 分层服务注入思想实现
    C语言:用一级指针访问二维数组的max,min,并打印
    喜报|Authing 入选 CNCF Landscape 云原生技术图谱
    Linux 系统之 CentOS 和 Ubuntu 的区别
    elementui 表格自动滚动
    OSI参考模型个人总结
    Business eXtreme 5 企业级报表和数据分析工具
    简单介绍一下Serverless和Faas
    207、室外远距离点对点无线网桥组网方案
    区块链技术的应用场景和优势
  • 原文地址:https://blog.csdn.net/qq_53010164/article/details/124860489