硬核两万字文章带你c++入门(代码片段)

小赵小赵福星高照~ 小赵小赵福星高照~     2023-01-08     767

关键词:

C++入门

C++关键字

C语言关健字32个,C++关键字63个关键字

asmdoifreturntrycontinue
autodoubleinlineshorttypedeffor
booldynamic_castintsignedtypeidpublic
breakelselongsizeoftypenamethrow
caseenummutablestaticunionwchar_t
catchexplicitnamespacestatic_castunsigneddefault
charexportnewstructusingfriend
classexternoperatorswitchvirtualregister
constfalseprivatetemplatevoidtrue
const_castfloatprotectedthisvolatilewhile
deletegotoreinterpret_cast

C++是在C的基础上发展起来的,C++是兼容C的大多数的语法

命名空间

C++是怎么输出hello world的呢?和C语言有什么区别呢?

#include<iostream>
using namespace std;
int main()

    cout<<"hello world"<<endl;
    return 0;

我们创建一个C++文件后,写出上面代码就会在屏幕上打印hello world,那么上面代码中的using namespace std;是什么意思呢?这就是我们要讲的命名空间。

C++增加命名空间是为了解决C语言的不足,在一段C语言代码中,我们编译下面的代码会报错:

#include<stdio.h>
#include<stdlib.h>
int rand =10;
int max=10;
int main()

    printf("%d\\n",rand);
    return 0;

那么为什么会报错呢?是因为我们包含了stdlib.h这个头文件

这是一个命名重定义的问题,在我们编写C语言代码时,我们定义变量名时,可能与库函数中的命名冲突了,实际大型项目开发,还存在同事之间定义的变量、函数、类型命名冲突等等

所以为了弥补C语言的不足,C++提出了命名空间来解决命名冲突的问题

那么命名空间是怎么定义的呢?

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对即可,中即为命名
空间的成员。注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中

#include<stdio.h>
#include<stdlib.h>
namespace Z//会将外面的定义的变量隔离开来,定义了一个命名空间域,名字叫Z

    //可以定义变量、函数、类型
    int rand = 10;

int main()

    printf("%d\\n",Z::rand);//::域作用限定符
    return 0;

::是域作用限定符,通过这个我们可以找到命名空间中的定义的变量或者其他东西,这样我们就可以定义rand,并可以成功的打印它

我们也可以在里面定义函数:

#include<stdio.h>
#include<stdlib.h>
namespace Z//会将外面的定义的变量隔离开来,定义了一个命名空间域,名字叫Z

    //可以定义变量、函数、类型
    int Add(int left,int right)
    
        return left+right;
    

int main()

    Add(1,2);//直接调用他找不到,因为他只会在全局里面找,不会去域里面
    Z::Add(1,2);
    return 0;
  

这里需要注意的是直接调用他找不到,因为他只会在全局里面找,不会去域里面,所以我们需要利用域作用限定符去调用Add函数

命名空间还可以嵌套定义:

namespace Z//会将外面的定义的变量隔离开来,定义了一个命名空间域

    //定义变量、函数、类型
    int rand = 10;
    int Add(int left,int right)
    
        return left+right;
    
    namespace S//会将外面的定义的变量隔离开来,定义了一个命名空间域
    
        int Sub(int left,int right)
        
            return left-right;
        
    

int main()

    Add(1,2);//直接调用他找不到,因为他只会在全局里面找,不会去域里面
    Z::Add(1,2);
    Z::S::Sub(1,2);
    return 0;
  

我们在调用嵌套的命名空间的函数时,比如我们现在调用Sub函数,我们需要用Z::S::Sub(1,2);去调用该函数

在一项工程当中不同文件的命名空间的定义名字可以相同,并且该命名空间中的定义编译器会合并在一起,比如:

在Add.cpp这个文件中我们定义了一个命名空间

namespace Z//会将外面的定义的变量隔离开来,定义了一个命名空间域

    //定义变量、函数、类型
    int rand = 10;

在Add.h中我们定义了命名空间

namespace Z//会将外面的定义的变量隔离开来,定义了一个命名空间域

    //定义变量、函数、类型
    int Add(int left,int right)
    
        return left+right;
    

编译器会将这两个文件中定义的空间进行合并:

namespace Z//会将外面的定义的变量隔离开来,定义了一个命名空间域

    //定义变量、函数、类型
    int rand = 10;
    int Add(int left,int right)
    
        return left+right;
    

那么我们如何使用命名空间的东西呢?

有三种方式:

  • 1、全部直接展开到全局
namespace Z//会将外面的定义的变量隔离开来,定义了一个命名空间域

    //定义变量、函数、类型
    int rand = 10;
    int Add(int left,int right)
    
        return left+right;
    

using namespace Z;//将Z命名空间展开
int main()

    Add(1,2);
    return 0;

优点是用起来方便。缺点是把自己的定义暴露出去了,导致命名污染

using namespace std;//std是包含C++标准库的命名空间

std是包含C++标准库的命名空间,这里其实就将C++标准库展开了,这就解释了我们开头打印hello world时,前面为什么有一个using namespace std;这里就很好的解释了这个代码的意思

  • 2、访问每个命名空间中的东西时,指定命名空间
namespace Z//会将外面的定义的变量隔离开来,定义了一个命名空间域

    //定义变量、函数、类型
    int rand = 10;
    int Add(int left,int right)
    
        return left+right;
    

int main()

    Z::Add(1,2);
    return 0;

优点:不存在命名污染。缺点:用起来麻烦,每个都得去指定命名空间

  • 3、将命名空间中常用的展开
namespace Z//会将外面的定义的变量隔离开来,定义了一个命名空间域,名字叫Z

    //可以定义变量、函数、类型
    int rand = 10;
    int Add(int left, int right)
    
        return left + right;
    

int main()

    using Z::Add;
    return 0;

不会造成大面积的污染,也可以解决每个都指定命名空间的问题,这是一个将1和2折中的解决方案


C++输入&输出

这又回到了我们开始提到的C++是怎么输出hello world到屏幕上的,我们来看下C++是如何来实现的:

#include<iostream>
//展开常用的
using std::cout;
using std::endl;
int main()

    cout<<"hello world"<<endl;
    return 0;

按照上面我们所讲的,std是一个命名空间,命名空间中有我们要使用的cout和endl,所以我们这里只将常用的展开,但是在日常学习中,我们并不需要像项目中那么规范,我们可以直接将std命名空间展开。

#include<iostream>
//展开常用的
using namespace std;
int main()

    cout<<"hello world"<<endl;
    return 0;

我们了解了输出,那么C++是如何进行输入的呢?我们使用cin标准输入,看下面代码:

#include<iostream>
//展开常用的
using namespace std;
int main()

    int n;
    cin >> n;//>>输入运算符 流提取运算符
    int* n = (int*)malloc(sizeof(int)*n);
    for(int i=0;i<n;i++)
    
        cin>>a[i];
    
    for(int i=0;i<n;i++)
    
    	cout << a[i]<<" ";//<<输出运算符/流插入运算符  可以连续输出
    
    cout<<endl;
    //等价于
    cout<<"\\n";

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

我们在输出时可以连续输出,cout<<endl;实际上就等价于cout<<"\\n"。

另外C语言输入输出时需要指定类型的,C++不用指定类型可以自动识别类型:

double* n = (double*)malloc(sizeof(double)*n);
for(int i=0;i<n;i++)

    cin>>a[i];//自动识别类型

int main()

    int i=2;
    double d = 1.111;
    int* pi = &i;
    cout<<i<<endl
    cout<<d<<endl
    cout<<pi<<endl;
    return 0;

在写C语言printf和scanf函数时,前面我们需要指定输入输出的格式,而C++则是自动识别类型。

而在下面的场景中,用printf会更好一点

struct Student

	char name[10];
	int age;
;
int main()

	struct Student s =  "张三", 18 ;
	cout << "名字:" << s.name << " " << "年龄:" << s.age << endl;
	printf("名字:%s 年龄:%d\\n", s.name, s.age);
	return 0;


缺省参数

缺省参数概念

所谓缺省参数,顾名思义,就是在声明函数的某个参数的时候为之指定一个默认值,在调用该函数的时候如果采用该默认值,你就不需要传参。可以传参数,也可以不传,如果不传,函数参数用缺省的

我们看下面代码:

#include<iostream>
using namespace std;
void TestFunc(int a = 0)//参数缺省值

    cout<<a<<endl;

int main()

    TestFunc();//不传会用默认值
    TestFunc(10);//传了就缺省参数就没用了
    return 0;

我们可以看到没有传参的使用了缺省值,而传参的就忽略缺省值了,只考虑传过来的参数

那么缺省参数有什么用呢?

我们在数据结构中,设计栈这个数据结构时:

void StackInit(struct Stack* ps, int defaultCP)
// 假设我明确知道这里至少要存100个数据到st1里面去
struct Stack st1; 
StackInit(&st1, 100);

如果我们假设明确知道一个栈st1最少使用100个空间,我们给栈的容量初始化为100,所以这时我们在栈初始化这个接口引入一个参数defaultCP,我们想要弄100个容量,我们直接传参100就可以了。

但是当我们不能明确知道栈st2的需要使用的空间,如果上面不设置缺省参数,那么我们必须要传一个值进去,那我们传的值大了的话,会造成空间浪费,传的小了的话会不够用,那么此时这个缺省参数就派上用场了,我们这样定义:

void StackInit(struct Stack* ps, int defaultCP = 4)
struct Stack st2; 
StackInit(&st2);

不传值时默认它为4,不够了再进行扩容就可以了


缺省参数分类

全缺省参数

void TestFunc(int a = 10, int b = 20, int c = 30)

    cout<<"a = "<<a<<endl;
    cout<<"b = "<<b<<endl;
    cout<<"c = "<<c<<endl;

全缺省参数顾名思义就是全部都有缺省参数,我们在传参时不能间隔着传,比如:

TestFunc(100, ,50);//这样是错误的

编译器是不允许这样进行传参的

半缺省参数

void TestFunc(int a, int b = 10, int c = 20)

    cout<<"a = "<<a<<endl;
    cout<<"b = "<<b<<endl;
    cout<<"c = "<<c<<endl;

int main()

    //没有缺省的必须传参
    //半缺省参数必须从右往左依次来给出,不能间隔着给
    TestFunc(1);
    TestFunc(1,2);
    TestFunc(1,2,3);
    
    return 0;

注意:

  • 没有缺省的必须传参

比如这上面的a参数必须要传参

  • 半缺省参数必须从右往左依次来给出,不能间隔着给

比如:

void TestFunc(int a = 30, int b = 10, int c)

这样就是错误的,没有按照从右往左的顺序

也不能这样:

void TestFunc(int a = 30, int b, int c = 10)
  • 缺省参数不能在函数声明和定义中同时出现
//a.h
void TestFunc(int a = 10);
//a.c
void TestFunc(int a = 20)

// 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

  • 缺省值必须是常量或者全局变量
void TestFunc(int a = x);//error
  • C语言不支持(编译器不支持)

C语言是不支持缺省参数的。


函数重载

函数重载概念

重载函数是函数的一种特殊情况,为方便使用,C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者类型顺序)必须不同,也就是说用同一个函数完成不同的功能。

//函数重载
int Add(int left, int right)

	return left+right;

double Add(double left, double right)

	return left+right;

long Add(long left, long right)

	return left+right;

int main()

    Add(10,20);//会调用对应参数类型的函数
    Add(10.0,20.0);
    Add(10L,20L);
    
    return 0;

函数重载面试:为什么C++支持函数重载,而C语言不支持?

C编译器,直接用函数名关联,当函数名相同时,它无法区分

C++如何支持重载呢?

函数名修饰规则:不能直接用函数名对函数进行修饰,代入参数特点修饰,函数名相同,只要参数不同,修饰出来的名字就不同,就可以区分两个函数了,就支持重载了,把定义在文件中的函数调用,地址找到(符号表里找),链接在一起,所有.o文件进行合并,生成一个执行文件。

函数重载:

要求参数不同,因为参数不同修饰出来的名字就不同

编译器能不能实现函数名相同参数相同返回值不同,就能构成重载?

不行

int func();//-> _Z4ifunc
double func();//-> _Z4dfunc

如果把返回值带进修饰规则,那么编译器层面是可以区分的。

但是语法调用层面,无法区分,带有严重的歧义!

func();调用时,到底是调用哪个呢?这是不知道的,所以不能


名字修饰

extern"C"

C++实现编写成动态库或者静态库,写一个C++的程序去调用这个库是没问题的,但是我们写一个C程序就不行,程序是用因为链接时会有问题,比如静态库有一个函数void* tcmalloc(size_t n) (谷歌提供的更高效替代malloc的库)

C程序在链接时,直接用函数名tcmalloc去找函数的地址,因为C++有名字修饰,而生成的符号表是:0x662521:_Z8tcmallocui,而C++是用_Z8tcmallocui去找的,C++可以找到,因为该动态库是C++写的,而C语言程序就找不到

那么有什么方式能让C程序和C++的程序都能用这个C++的库呢?

C++就出现了extern"C",在声明tcmalloc这个函数时在前面加extern"C" void* tcmalloc(size_t n),此时符号表按C语言修饰规则就成为了0x662521:tcmalloc,这时C程序就能正确的找到了,C++程序中有tcmaloc函数的声明,发现有extern"C",就按C语言修饰规则去找,因为C++兼容C,所有C的修饰规则它也是知道的。

总结:

当C的程序和C++的程序都想调用一个C++实现的模块时,C++可以调,但C语言不能调,那么两个都想调用时,我们就在C++实现的该模块里面函数声明时在前面加extern"C",那么生成的符号表里面就不对这个函数进行修饰了,这时C语言实现的程序就可以调用它了,紧接着C++程序在调用时,因为C++是兼容C的,发现函数的声明有extern"C",所以就在链接的时候按C的规则去找这个模块的函数

面试题:

下面两个函数能不能构成函数重载?

void TestFunc(int a = 10)

	cout<<"void TestFunc(int)"<<endl;

void TestFunc(int a)

	cout<<"void TestFunc(int)"<<endl;

void TestRef()

    int a = 10;
    int& ra = a;//<====定义引用类型
    printf("%p\\n", &a);
    printf("%p\\n", &ra);

不行,重载必须要函数参数列表不同,因为这样才在名字修饰时有所区别,能够区分函数。

C语言中为什么不能支持函数重载?

C编译器,直接用函数名关联,当函数名相同时,它无法区分

C++中函数重载底层是怎么处理的?

在链接阶段,有符号表的合并与重定义,那么函数名相同那么他们重载函数符号表会不会冲突呢?答案是不会的,C++有自己的名字修饰规则,比如重载函数参数类型一个为int,另一个为float,在名字修饰时,就会将这些信息代入进去,这样就区分了两个函数。

C++中能否将一个函数按照C的风格来编译?

可以,在该函数前面加extern"C"即可,C++程序中有函数的声明,发现有extern"C",就按C语言修饰规则去找,因为C++兼容C,所有C的修饰规则它也是知道的


引用

引用概念

引用并不是新定义一个变量,而是给已存在的变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间

类型& 引用变量名 = 引用实体;

#include<iostream>
using namespace std;
int main()

    int a=0;
    int& ra = a;//ra是a的引用,引用也就是别名,a再取了一个名称ra
    return 0;

我们可以重复引用,也可以对本身是引用的变量名再次进行引用,例如:

int main()

    int a狂肝两万字带你用pytorch搞深度学习!!!(代码片段)

深度学习基础知识和各种网络结构实战...狂肝两万字带你用pytorch搞深度学习!!!深度学习前言一、基本数据:Tensor1.1Tensor的创建1.2torch.FloatTensor1.3torch.IntTensor1.4torch.randn1.5torch.range1.6torch.zeros/ones/empty二、Tenso 查看详情

web三件套两万字带你入门css(代码片段)

文章目录1.CSS基本介绍1.1认识CSS1.2CSS基本语法规范1.2.1基本语法1.2.2样式风格1.2.3样式大小写1.2.4空格规范1.3引入方式1.3.1内部样式1.3.2行内样式1.3.3外部样式2.选择器2.1选择器的功能2.2选择器的种类2.3基础选择器2.3.1标签选择器2.3.2... 查看详情

超硬核知识:两万字总结《c++primer》要点!

对于想要入门C++的同学来说,《C++Primer》是一本不能错过的入门书籍,它用平易近人的实例化教学激发学生的学习兴趣,帮助学生一步步走进C++的大门。在本文中,作者Jacen用两万多字总结了《C&... 查看详情

手把手写c++服务器(34):高并发高吞吐io秘密武器——epoll池化技术两万字长文(代码片段)

...#43;服务器(31):服务器性能提升关键——IO复用技术【两万字长文】介绍了IO复用技术,其中重点比较了select、poll、epoll三种IO复用技术,由于epoll池底层数据是红黑树& 查看详情

手把手写c++服务器(31):服务器性能提升关键——io复用技术两万字长文(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:Linux中素有“万物皆文件,一切皆IO”的说法。前面几讲手撕了CGI网关服务器、echo回显服务器、discard服务的代码,但是这... 查看详情

❥十大排序算法❥爆肝两万字保姆级教程(文字解析+图解+代码实现+例题)(代码片段)

适合新人的入门级文章什么是算法?十大排序算法🎈冒泡排序解析代码实现例题🎈选择排序解析代码实现例题🎈插入排序解析代码实现例题🎈希尔排序解析代码实现例题🎈归并排序解析代码实现例题... 查看详情

两万字vue基础知识总结,小白零基础入门,跟着路线走,不迷路(建议收藏)(代码片段)

立志存高远,笃行践初心  三更灯火五更鸡,正是男儿读书时。黑发不知勤学早,白首方悔读书迟。立志,标定人生方向;奋斗,创造人生价值,二者相辅相成,互相促进。大部分程序员的「目标... 查看详情

手把手写c++服务器(31):服务器性能提升关键——io复用技术两万字长文

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:Linux中素有“万物皆文件,一切皆IO”的说法。前面几讲手撕了CGI网关服务器、echo回显服务器、discard服务的代码,但是这几个一次只能监听... 查看详情

springioc基于xml的di详细配置全解两万字(代码片段)

详细介绍了SpringIoC基于XML的DI详细配置全解。前面的文章SpringIOC容器的概念以及基于XML的IoC装配中,我们学习了IoC容器的概念以及依赖注入的两种方式,现在我们来看看一些更详细的配置。本文基于Spring5.2.8,案例基... 查看详情

c++基础一c++基础入门(20000字掌握c++基础知识)(代码片段)

文章目录1.C++关键字2.命名空间2.1什么是命名空间?2.2为什么需要命名空间?2.3如何定义命名空间?2.4如何使用命名空间?2.5深入理解命名空间3.C++输入&输出3.1如何区分C和C++文件3.2cin和cout详解... 查看详情

c++基础一c++基础入门(20000字掌握c++基础知识)(代码片段)

文章目录1.C++关键字2.命名空间2.1什么是命名空间?2.2为什么需要命名空间?2.3如何定义命名空间?2.4如何使用命名空间?2.5深入理解命名空间3.C++输入&输出3.1如何区分C和C++文件3.2cin和cout详解... 查看详情

❤️通宵爆肝两万字xpath教程+实战练习❤️(代码片段)

文章目录一、必看内容!!!1)简短介绍2)必备知识3)为什么我要写这篇文章?4)强烈推荐教程专栏二、开始使用xpath2.1常见的HTML操作2.2常见XML操作2.2.1选择一个元素2.2.2选择文字2.3浏览器使用xpa... 查看详情

❤️爆肝3天!两万字图文sql零基础入门,不怕你学不会,就怕你不收藏!❤️(代码片段)

目录📚前言🌴SQL介绍🌼什么是SQL🌀SQL的类型🌵学习SQL的作用🍄数据库是什么🐥SQL基础语言学习🐤CREATETABLE–创建表🐑INSERT–插入数据🐼SELECT–查询数据🐫DISTINCT–去除重复值Ǵ 查看详情

❤️爆肝3天!两万字图文sql零基础入门,不怕你学不会,就怕你不收藏!❤️(代码片段)

目录📚前言🌴SQL介绍🌼什么是SQL🌀SQL的类型🌵学习SQL的作用🍄数据库是什么🐥SQL基础语言学习🐤CREATETABLE–创建表🐑INSERT–插入数据🐼SELECT–查询数据🐫DISTINCT–去除重复值Ǵ 查看详情

两万字硬核解析树与二叉树所有基本操作(包含堆,链式二叉树基本操作及测试代码,和递归函数的书写方法)(代码片段)

文章目录前言树什么是树树的基本概念树的实现方式二叉树什么是二叉树两种特殊的二叉树满二叉树完全二叉树二叉树的性质二叉树的存储堆什么是堆堆的分类及特点堆的作用堆的实现堆的插入(建立大堆)堆的删除堆... 查看详情

建议收藏两万字深度解读指针,学好指针看这一篇文章就够了(代码片段)

前言大家好,我是努力学习的少年,今天这篇文章是专门写关于指针的知识点,因为指针内容比较多,所以我将指针的这篇文章我将它分为两部分,第一部分是基础篇,是从零开始学习一些基本概念,... 查看详情

两万字长文,彻底搞懂kafka(代码片段)

点击关注公众号,实用技术文章及时了解1、为什么有消息系统1、解耦合2、异步处理例如电商平台,秒杀活动。一般流程会分为:风险控制库存锁定生成订单短信通知更新数据通过消息系统将秒杀活动业务拆分开࿰... 查看详情

☀️福利向:⚡️万字图文⚡️带你vagrant从入门到超神!❤️(代码片段)

Vagrant从入门到超神🌲前言🌲🌻Vagrant简介🌻❤️入门玩法:Vagrant安装❤️⚡️初阶玩法:Vagrant常用命令⚡️1️⃣Vagrant基础命令2️⃣VagrantBox管理3️⃣Vagrant虚拟机系统命令4️⃣Vagrant插件管理🌀进... 查看详情