此篇为C++入门首篇,学习的主要是:命名空间 || C++输入和输出 || 缺省参数 || 函数重载 || 引用 || extern "C" || 内联 || auto关键字(C++11) || 基于范围的for循环(c++11) || 指针空值nullptr(C++)
等内容,此次让小新带领大家一起走向C++第一步


壹 ----  为什么要用命名空间 ?

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

0x01 :命名冲突域

①C++是兼容C的,所以cpp中一样可以使用c的语法

#include<stdio.h>
int rand = 0;
int main()
{printf("hello yongheng\n");printf("%d\n,rand");
}

②在stdlib库中有一个产生随机数的函数rand()

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int main()
{printf("hello yongheng\n");printf("%d\n",rand);
}

③我们知道 #include包含头文件,头文件会被展开来,此时stdlib库展开,其中包含了一个rand的函数,如果此时再定义一个rand全局变量会是什么结果呢?

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
int rand = 0;
int main()
{printf("hello yongheng\n");printf("%d\n",rand);
}

结果马上揭晓:
没错就是下面这个结果


④那么这个问题该如何解决呢?

此时的良方就是运用命名空间,因为它解决的就是命名冲突问题,namespace中的rand还是全局变量,此时namespace的作用就是为了防止stdlib库中的函数rand与变量rand命名冲突

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
namespace yongheng
{int rand = 0;
}
int main()
{printf("hello yongheng\n");printf("%d\n",rand);
}

⑤可以通过 作用域限定符进行验证,是否可以显示namespace中变量rand,需要了解的是,机器首先会在局部中去找,然后再到全局中去找,如果不使用作用域限定符的话是不会去namespace中去寻找rand变量的,因为namespace创建的是一个域,相当于将rand变量包裹在这个域中了

0x02: 命名空间的内容
       ①变量 ②函数 ③结构体 等

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
namespace yongheng
{int rand = 0;//变量int Add(int left,int right)//函数{return left + right;}struct Node //结构体{struct Node* next;int val;};}
int main()
{printf("%d",yongheng::rand);yongheng::Add(1,2);struct yongheng::Node node;
}

0x03: 命名空间可以嵌套命名空间

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
namespace N1
{namespace N2{int Add(int left,int right){return left + right;}}}
int main()
{printf("%d",N1::N2::Add(1,2)); // 3
}

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

示例如下:

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

0x05  命名空间的使用

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

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

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

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

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

0x06:  三种命名空间的使用示例

   iostream库包含了输入输出流,std是一个标准库命名空间,里面包含了cout(流插入)和cin(流提取)等

①将std命名空间展开(适合练习的时候使用)

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;//
int main()
{cout << "hello world" << endl;return 0;
}

②std命名空间中包含cout与cin和endl等,所以可以指定展开

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
int main()
{std::cout << "hello world" << std::endl;return 0;
}

③将常用的展开

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using std::cout;
using std::endl;
int main()
{cout << "hello world" << endl;return 0;
}

贰 ----  C++输入和输出

0x07  cout和cin的运用

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
int main()
{int i = 0;int j = 3.14;cout << i << ' ' << j << endl;cin >> i >> j;cout << i << ' ' <<  j  << endl;
}

  解析如下:

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

0x08: C语言和C++语言的输出比较
示例如下:

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

叁 ---- 缺省参数

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

0x09 全缺省参数

示例如下:

全缺省参数: 即上述a,b,c三个都有缺省参数

0x10 半缺省参数

示例如下:

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

3. 缺省值必须是常量或者全局变量
4. C 语言不支持(编译器不支持)

肆 ----  函数重载

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

0x11  参数类型不同

0x12  参数个数不同

0x13  参数顺序不同

0x14  是否构成重载的几种情况

①返回值不同,不能构成重载

②缺省值不同,不能构成重载

③构成重载,但是一个存在默认参数,一个不存在默认参数,如下示例,会产生二义性

0x15 为什么C语言不支持函数重载,而C++支持函数重载?

   解析如下:

首先我们先建立一个头文件 fac.h,再建立俩个文件fac.c和test.c ,然后我们我们回顾一下编译器编译的整个过程:
1. 预处理: 进行的是头文件展开,宏替换,条件编译,去掉注释等等 生成fac.i 和  test.i
2. 编译: 进行检查语法,生成汇编代码  生成fac.s 和  test.s
3. 汇编: 汇编代码转换成二进制机器码  生成fac.o 和 test.o
4. 链接: 生成a.out

① c语言不支持函数重载:因为在编译的时候,俩个重载函数,函数名相同,在fac.o符号表中存在歧义和冲突,其次链接的时候也存在歧义和冲突,因为它们都是直接使用函数名去标识和查找的,而重载函数,它们的函数名又是相同的,所以不支持重载
②c++支持重载函数:因为C++的目标文件符号表中不是直接用函数名来表示和查找的,而是使用函数名修饰规则去查找(在不同编译器下不同),有了这个函数名修饰规则,只要参数不同,fac.o符号表里面重载的函数就不存在二义性和冲突了,所以在链接的时候,test.o的main函数里面去调用这俩个重载的函数,查找他们的地址时,也是非常明确,且没有歧义的

需要注意的是:
①如果当前文件有函数定义,那么在编译的时候就把地址填上了
②如果在当前只有函数的声明,那么定义肯定是在其他的.cpp中,编译时是没有地址的,只能在链接的时候去其他的.o符号表中根据函数名修饰规则去找,这是链接部分所做的
③函数名修饰规则: _z + 函数名长度 + 函数名 + 参数首字母 如 函数名 f() :   _z1fv

伍 ---- 引用

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

实例如下:

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

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

0x16 引用特性

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

②同一个域中不能有相同的名字

d是a的小名,那就不能有一个人的大名叫d,即同一个域中不能有相同的名字

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

④引用与指针的对比

指针可以看作是一个渣男,第一次喜欢A,第二次又喜欢B. 而引用一旦喜欢上A,他就不可能喜欢B了

0x17 传指针、传引用、传值

//传指针
void Swap(int* p1,int* p2)
{int temp = *p1;*p1 = *p2;*p2 = temp;
}
//传引用,引用做参数,把a,b起个别名
void Swap(int& r1,int& r2)
{int temp = r1;r1 = r2;r2 = temp;
}
//传值
void Swap(int r1, int r2)
{int temp = r1;r1 = r2;r2 = temp;
}
//他们三个构成重载(类型不同)
//但是Swap(a,b);调用是存在歧义,他们不知道调用,传值还是传引用int main()
{int a = 10, b = 20;Swap(&a,&b);/*Swap(a,b);*/
}

0x18 传引用作参数的应用

0x19 传引用作返回值

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

0x20 常引用

//常引用
int main()
{//权限放大: a是只读的,而b是a的引用,竟然是可读可写的,那是不可以的const int a = 10;int& b = a;//权限不变: c和d都是只读的 ,可以const int c = 10;const int& d = c;//权限缩小:f是只读的,可以int e = 10;const int& f = e;
}

0x21  const在引用中的应用

上图解析:

第一个图解析:d是double类型,产生临时变量,通过隐式类型转换,变为Int类型,第二个是d相当于是不可修改+int 类型,所以要加const

第二个图解析:x1 + x2 这个结果会产生一个临时变量,临时变量是具有常属性的,不可修改,所以 所以这个临时变量可以看作是一个int类型的数据+不可修改这个特性,所以ret 前面要加const

0x22 引用和指针的不同点

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

六 ---- extern "C"

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

0x23 c++项目调用C静态库

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

步骤如下:

0x24 C工程调用C++库步骤

解析:只有C++认识extern "C",静态库为C++文件时,告诉机器按C的修饰规则去找,而C项目中,头文件展开,直接就是函数名

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

柒 ---- 内联

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

0x25   C语言通过宏(一种替换,不需要建立栈帧)

#define  Add(x,y) ((x)+(y))
//最好把((x)+(y))中x和y括号都加上,否则比如(x+y),10*Add(3,4),会替换为10 * 3+4,会导致出错int main()
{cout << Add(1, 2) << endl;return 0;
}

0x26  inline 内联函数

C++ 以 inline 修饰 的函数叫做内联函数, 编译时 C++ 编译器会在 调用内联函数的地方展开 ,没有函数压栈的开销, 内联函数提升程序运行的效率。

查看方式:

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

0x27  inline特性

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

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

在早期 C/C++ 中 auto 的含义是:使用 auto 修饰的变量,是具有自动存储器的局部变量 ,但遗憾的是一直没有人去使用它,大家可思考下为什么?
C++11 中,标准委员会赋予了 auto 全新的含义即: auto 不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器, auto 声明的变量必须由编译器在编译时期推导而得

实例如下:

0x28  实例

int main()
{const int a = 0;int b = 0;//自动将a的类型推到c,  auto f = a;//intauto c = &a;//int const*auto d = 'A';//charauto e = 10.11;//double//f,d,e的类型是const的,具有常性,但是auto会忽略这个const//typeid打印变量的类型cout << typeid(f).name() << endl;cout << typeid(c).name() << endl;cout << typeid(d).name() << endl;cout << typeid(e).name() << endl;auto e;//err,auto定义变量时必须对其进行初始化
}
[注意]
使用 auto 定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导 auto 的实际类型 。因此 auto 并非是一种 类型 的声明,而是一个类型声明时的 占位符 ,编译器在编译期会将 auto 替换为 变量实际的类型

0x29  auto的使用

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

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

        ②   在同一行定义多个变量

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

0x30  auto 不能推导的场景

① auto不能作为函数的参数

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

玖 ---- 基于范围的for循环(c++11)

0x31 范围for的语法

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

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

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

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

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

 for (auto& e : array){e++;}

0x32 范围for 的使用条件

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

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

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

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

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

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

大家可以猜想一下,下面的程序是什么结果?

void f(int)
{cout << "f(int)" << endl;
}
void f(int*)
{cout << "f(int*)" << endl;
}
int main()
{f(0);f(NULL);f((int*)NULL);return 0;
}

没错,运行出来就是如下结果:

解析:
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

【C++入门基础篇】---- 万字解析相关推荐

  1. 【目录】Python 入门基础篇 <(^-^)>

    Python 入门基础篇 一.关于Python的介绍与准备工作 Python基础介绍 Jupyter notebook基础介绍 Jupyter notebook打开问题 Jupyter noteboo ...

  2. typescript箭头函数参数_Typescript 入门基础篇(一)

    Typescript 基础 Typescript是Javascript的一个超集.以下typescript简称为ts, 此文章主要是对ts官网文档的一个简化,缩短学习基础时间. 类型基础 ts 的类型 ...

  3. 你所需要的java基础篇深入解析大汇总

    java基础篇深入解析大总结 java基础(一) 深入解析基本类型 java基础(二) 自增自减与贪心规则 java基础(三) 加强型for循环与Iterator java基础(四) java运算顺序 ...

  4. 小程序开发入门基础篇-张代浩-专题视频课程

    小程序开发入门基础篇-1995人已学习 课程介绍         采用小程序wepy框架,初级讲解如何搭建小程序的开发环境,创建工程,语法介绍.开发调试等,课程采用实战代码案例作为教材,通俗易懂,简单 ...

  5. python视频教程推荐it教程网_Python视频教程之入门基础篇_IT教程网

    资源名称:Python视频教程之入门基础篇 资源目录: [IT教程网]320b96cae58124db5fb6e7c5df99aefc [IT教程网]699434136852f34ec720f2a34 ...

  6. Python入门基础篇 No.8 —— 时间的表示_unix时间点_毫秒_time模块

    Python入门基础篇 No.8 -- 时间的表示_unix时间点_毫秒_time模块 文章目录 Python入门基础篇 No.8 -- 时间的表示_unix时间点_毫秒_time模块 前言 一.时间 ...

  7. vue实战入门基础篇五:从零开始仿门户网站实例-关于我们实现

    上一篇:vue实战入门基础篇四:从零开始仿门户网站实例-网站首页实现https://blog.csdn.net/m0_37631110/article/details/123045334 一.目录 第 ...

  8. vue实战入门基础篇二:从零开始仿门户网站实例-开发框架搭建

    上一篇:vue实战入门基础篇一:从零开始仿门户网站实例-前期准备工作 vue实战入门基础篇二:从零开始仿门户网-2022-2-23 21:00:27 一.目录 第一篇:前期准备工作 第二篇:开发框架搭 ...

  9. RAID技术详细解答之一:入门基础篇

    本文所要讲解的RAID技术起初主要应用于服务器高端市场,但是随着个人用户市场的成熟和发展,正不断向低端市场靠拢,从而为用户提供了一种既可以提升硬盘速度,又能够确保数据安全性的良好的解决方案.IDE 磁 ...

最新文章

  1. 任正非最新署名文章:不要因为美国打压而放弃全球化战略
  2. crypto安装_KubeEdge 完整安装 amp; 部署小指南
  3. 【Verilog HDL】第三章 reg和net及其一组类型的区别——充分运用实验思维
  4. 这5种思维模式,大牛产品经理都在用
  5. 深度评测阿里云、百度云、腾讯云和华为云
  6. 微信小程序 开发文档
  7. 1208: [HNOI2004]宠物收养所
  8. BubbleSort C#
  9. 【AI数学原理】函数求导(精髓篇)
  10. 鸡啄米:C++编程入门系列之前言
  11. 微信聊天记录迁移及故障修复
  12. android 应用更新安装时出现《 文件包与具有同一名称的现有文件包冲突》小记录
  13. 理解HTML HTTP API 和URL
  14. 收到FRDM-KL02Z
  15. Linux 系统安全应用
  16. 小白:从0开始捣鼓Ubuntu(中文输入法)
  17. JAVA后台如何处理客户端提交的二进制图片思路
  18. 转博答辩ppt_湖南大学博士学位答辩PPT.ppt
  19. 企业级用户画像:用户购物性别模型-USG和决策树算法
  20. Kotlin知识归纳(四) —— 接口和类

热门文章

  1. 广州车展开幕:超定律智能纯电SUV埃安Y全球首发
  2. 兼容Android 11 相机拍照,从相册中选择,裁剪图片
  3. 搭建vpn linux,vpn在linux下搭建
  4. 11月,匆匆而过,留下了遗憾(亚洲赛广州站)
  5. 华夏ERP没有找到新增功能
  6. AHCI sata设备初始化流程
  7. 迁移数据到历史表SQL
  8. 如何使用手机软件将图片转换为文字
  9. 氨基-八聚乙二醇Amino-PEG8-alcohol,352439-37-3
  10. js-打印1-100以内的质数(素数)