初识C++

写在前面

这是我第一次接触到C++,之前对它的大名可谓是如雷贯耳.这两天有人问我学习C++的感觉怎么样?对此我想说,很难.C++的细节很多,要求我们比学习C语言更加细心,我不是没有学习过OOP语言,但是刚开始接触到还是感觉有点吃力,不过这是一种很令人高兴的情况,这代表你在进步,要是我们在一直学习已经站掌握的知识,又怎么会提高我们呢?我学习C++的方法是通过看网上一些大佬讲解的视频,然后通过一些经典的书籍来补充,后面和大家分享一下博客,做些OJ题.这种方法对我来说还是挺不错的,大家要是没有方法,可以先试试这个,后面可以选择更加适合自己的方法.


什么是C++

这里我就不和大家分享了,一般我们看每一本书都会谈到C++的历史,我想先和大家来一起学习这门语言,后面到最后专门写一篇博客来专门谈谈历史的一些趣闻趣事,这在我们面试的时候可以和面试官聊聊.

这里我先提一点,C++可以认为是对C语言一些缺陷的补充,C++编译器支持C语言语言的90%以上,类似函数的命名,scanf,printf都是支持的.

如何创建一个C++文件

前面我和大家分享C语言时用的环境大部分都是VS2019,这里用来做C++集成开发环境还是可以的,创建文件的步骤和之前是一样的,就是在最后一步,我们的文件的后缀名是.cpp,这样VS就会调用C++的编译器.

image-20220426095213165

Hello word

我们精通各种语言的Hello word,谈个玩笑.Hello word在我心中有不一样的意义,它敲开了很多人对计算机的大门,在这里我们仍旧打印出这句话,标志着我们正式进入C++,大家对代码要是有什么不理解的话,先不要着急,后面我们会一一解剖。

#include <iostream>
using namespace std;

int main()
{
	cout << "hello world" << endl;
	return 0;
}

image-20220426102141445

命名空间

我们总是看到有人一写代码就把using namespace std;,这是什么?这是必须的吗?一般的人可能就会谈到记住就可以了,不用管原理,但是对于我们这些要进大厂的人来说,这是很重要的.

我们先来看一个例子,下面的代码可以跑过吗,为什么不能跑过?

void f()
{

}

int f = 0;

int main()
{
	printf("%d", f);
	return 0;
}

image-20220426103328288

我们可以看出,第一个f表示的是函数名,第二个是一个全部变量,这就给编译器一种误导,这在语法上就是一种错误.在C语言这种错误只能有一种解决方法,函数改名或者是变量改名,这就会造成这样一种情况:

函数说:你改名字吧,变量,我这都被调用几百次了.变量反驳到,我这都用了上千次了,你说谁改?这种情况我们该怎么办,在C语言中我们我们只能一个一个修改.但是在C++中我们可以通过一种命名空间的凡是来解决.有人可能会感到纠结,我会这么大意,让它们重名?开玩笑!是的,我们自己当然可以确定自己的的命名不重复,可是大家不要忘了,一个很大的程序是多人协作共同完成的,你可以确定你们几个人甚至几十个人命名不重复,谁都不能保证.

namespace

C++解决命名重复就是使用namespace关键字,这是C++新增的一个,通过这个关键字我们可以把自己定义的变量隔离出来,就是想箱子一样,每一个人拥有一个属于自己箱子,用到的时候,自己打开自己的箱子就可以了.我们后面具体在谈,下面先看一个例子.

我们都知道下面的代码会打印出什么结果,局部优先,结果就是0,但是这里我先要得到全局变量a的结果,也就是10,怎么弄?

int a = 10;

int main()
{
	int a = 0;
	printf("a = %d\n", a);
	return 0;
}

域作用限定符 ::

我们可以通过一个::来解决这个问题,我们可以命名空间就是一个箱子,::可以把箱子打开拿到里面的内容.箱子:: 箱子里面的东西,当箱子省略时,它回去全局变量区那寻找.这就是我们可以得到10的原因.

int a = 10;

int main()
{
	int a = 0;
	printf("a = %d\n", ::a);
	return 0;
}

image-20220426112249327

创建命名空间

现在我们就可以放开手和大家分享了,前面的铺垫一已经够我们现在继续说下去了,每一个人都可以创建自己的命名空间,这样的话我们几个人之间就可以不担心命名重复了,使用namespace就可以创建了.

张三和李四都独自创建了自己的命名空间,这样即使后面它的命名出现错误也不会报错

//张三的命名空间
namespace N1
{
	int a;
}

//李四的命名空间
namespace N2
{
	int a;
}
int main()
{
	N1::a = 10;
	N2::a = 20;
	printf("%d\n", N1::a);
	printf("%d\n", N2::a);
	return 0;
}

image-20220426144210662

这里我想谈谈下面几点,这些有助于我们理解命名空间

上面的变命名的变量是全局变量,在我们定义这两个变量后,会在内存中开辟不同的空间.

int main()
{
	printf("%p\n", &(N1::a));
	printf("%p\n", &(N2::a));
	return 0;
}

image-20220426144729196

命名空间里面可以有什么

这个问题很好,命名空间里面可以包含三类事物.

  1. 全局变量
  2. 函数
  3. 结构体

前面的全局变量已经和大家分享过了,这里从函数这个比较简单的说起.

和全局变量一样,我们为了防止函数命名重复,也可以把函数放到命名空间里面.

namespace byte
{
	void func()
	{
		printf("这是 byte 空间里面呢的函数\n");
	}
}

int main()
{
	byte::func();
	return 0;
}

image-20220426151559393

在来谈谈结构体,关于结构,要是很上面说的一样就有点落于俗套了,这个我在命名空间重复嵌套这里谈,会好好说说.

命名空间的嵌套

一个大型工程,里面可能还要分为很多组别,例如有数据组的,有测试组的,这样的话,我们希望可以对命名空间进行嵌套,这样就可以避免重复

下面就是重复嵌套的命名空间

namespace byte
{
	namespace data
	{
        int a;
	}
	namespace test
	{
        int a;
	}
}

这里谈一谈,但我们使用命名空间存储结构体会发什么什么?注意这里的结构体里面存储的是类型,不是变量.当然它可以存储结构体变量.

下面的的命名空间我们我们可以嵌套几个,里面就可以定义我们的结构体了.

namespace byte
{
	//数据组
	namespace data
	{
		struct ListNode
		{
			int val;
			struct ListNode* next;
		};
	}
	//缓存组
	namespace cache
	{
		struct ListNode
		{
			int val;
			struct ListNode* next;
		};
	}
}

命名空间名字重复

当我们定义的命名空间名字重复后,编译器会自动把他整合在一起,也是说名字一样的命名空间可以看作同一个空间.

namespace N1
{
	int a;
	int b;
}
namespace N1
{
	int a;
	int b;
}

image-20220426161429560

如何使用命名空间里面的元素

我想使用命名空间里面的元素,有没有办法内?

使用域作用限定符::

前面我们已经谈过域作用限定符::了,刚才的众多例子中也多次使用了这个域作用限定符,这个域作用限定符的作用就像是可以打开箱子一样,拿出里面的元素.这里具体谈谈.

namespace N1
{
	int a;
	int b;
}

int main()
{
	N1::a = 1;
	N1::b = 2;
	printf("%d %d", N1::a, N1::a);
	return 0;
}

image-20220426153818056

使用域作用限定符::,我们使用哪个就可以拿出来哪个,这种方法比较谨慎,安全性也比较高,不过有一个缺点就是,有一点麻烦.

打开全部的元素

前面谈了有一点麻烦,这里有一个简单的方法,我们可以直接打开命名空间里面的全不元素,大家一看就知道了.

打开命名空间里面的所有元素,直接使用这些就可以了.这样相当于没有定义命名空间.

namespace N1
{
	int a;
	int b;
}

using namespace N1;  //打开 N1 的命名空间
int main()
{
	a = 20;       //直接使用
	b = 10;       //直接使用
	printf("%d %d", a, b);
	return 0;
}

一些缺陷在下面谈.

打开部分元素

打开全部的元素这种也会带来一部分问题,当我们打开多个命名空间的时候,既有可能会出现命名一样的情况,这里编译器就会报错,这里也有一个折中的方法,用哪个就把它从名宁空间里面拿出来.

我们打开了变量 a,就可以把他放开了.

namespace N1
{
	int a;
	int b;
}

using N1::a;

int main()
{
	a = 20;       //直接使用
	N1::b = 10;   //不能直接使用
	printf("%d %d", a, N1::b);
	return 0;
}

image-20220426160840036

我们这里来个测试,大家应该可以看懂了.

namespace byte
{
	int a;
	//数据组
	namespace data
	{
		struct ListNode
		{
			int val;
			struct ListNode* next;
		};
	}
	//缓存组
	namespace cache
	{
		int b;
		struct ListNode
		{
			int val;
			struct ListNode* next;
		};
	}
}
using namespace byte;
using namespace byte::data;
using byte::cache::b;
int main()
{
	//用域作用限定符
	byte::a = 10;
	struct byte::cache::ListNode n1; //有没有struct都可以

	//打开  byte命名空间
	a = 10;
	struct data::ListNode n2;  //有没有struct都可以

	//打开  byte命名空间 和  data
	struct ListNode n3;
	
	//使用 cache的的部分元素
	b = 20;
	return 0;
}

image-20220426164551445


解释using namespace std;

现在我们就可以知道了他就是打开名为std命名空间,这句话不是必须的,我们也是这样用.

int main()
{
	std::cout << "hello word" << std::endl;
	return 0;
}

image-20220426165451551

C++输入和输出

我们都知道,C++ 支持C语言的很多语法,这里输入输出可以使用scanf和printf,但是C++也给出了一些其他的方法,而且更加方便,我们先来看看具体如何做.

#include <iostream>

using namespace std;

int main()
{
	int a = 0;
	double d = 0.0f;
	cin >> a;
	cin >> d;
	cout << a;
	cout << d;
	return 0;
}

image-20220426170956780

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

这里我要谈一些东西

  • 在使用pritnf时,我们需要指定数据的格式,但是上面这种方法不需要,编译器会自动识别并匹配.
  • cin是标准输入流,从键盘中中输入,cout是输出流,输出到屏幕上
  • >>和<<可以理解为方向哎u你,例如 cin>>a,就是数据从键盘上输入到a中.cout<<a就是a输出到屏幕上
  • 大家疑惑插入操作符<<和左移操作符一样,这实际上是操作符的重载。我们现在不需要了解这么多.

如何进行换行?,上面的结果我很不适应,可以换一个行吗? 可以的

using namespace std;

int main()
{
	int a = 0;
	double d = 0.0f;
	cin >> a;
	cin >> d;
	cout << "a = " <<a << '\n';
	cout << "d = " <<d << '\n';
	return 0;
}

image-20220426171827283

缺省函数

我们开始疑惑,函数就是函数,什么叫缺省函数.不知道大家知不知道备胎,就是有事的时候找你来帮忙,没事的时候就吊着你.缺省函数也是如此,我们先来看看.

这里的func就是一个缺省函数,如果我们不传入参数,a就会赋值10,就是那个备胎,传入了,就可以把备胎给扔掉了.

void func(int a = 10)
{
	printf("a == %d\n", a);
}
int main()
{
	func();
	func(20);
	return 0;
}

image-20220426174251594

缺省函数的应用

这里我给出一个应用,我们在之前分享数据结构的时候,经常会出现初始化的情况,这里我先义队列为例子

这样我们初始化时就不需要给函数传入多少个空间,一当完成初始化就会有4个数据的空间,第一次插入的时候就不需要扩容了.

typedef struct Queue
{
	int* elem;
	int cap;
	int size;
} Queue;

void QueueInit(Queue* ps, int cap = 4)
{
	ps->elem = (int*)malloc(sizeof(int) * cap);
	ps->cap = cap;
	ps->size = 0;
}
int main()
{
	Queue qu;
	QueueInit(&qu);
	return 0;
}

多参数的缺省函数

只有一个函数的缺省函数可能体现不出来特性,下面我们使用三个参数的.

全部缺省

using namespace std;

void func(int a = 1, int b = 2, int c = 3)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}
int main()
{
	func();
	cout << endl;
	func(111);
	cout << endl;

	func(111,222);
	cout << endl;

	func(111,222,333);
	return 0;
}

image-20220426175856935

部分缺省

对于多参数的函数而言,我们可以缺省一部分的参数,不过参数缺省还需要一定的要求.

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

void func(int a,int b = 2, int c = 3)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}

image-20220426180234587

缺省函数的注意事项

我们来看看这些注意事项

  • 半缺省参数必须从右往左依次来给出,不能间隔着给
  • 缺省参数不能在函数声明和定义中同时出现
  • 缺省值必须是常量或者全局变量

第一个我们已经谈过了,这里主要谈第二条,我么缺省函数在声明的时候如何声明

void func(int a = 30);
void func(int a = 30) 
{
	cout << a << endl;
}

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

image-20220426181709140

当函数声明遇到函数声明和函数实现的时候,编译器不到使用哪个缺省值,即使有时他们是相同的.这样就会报错.这里我来谈谈

声明给,定义不给

我是使用的三个文件来演示的,

//Test.h文件中
#include <iostream>

using namespace std;

void func(int a = 20);

//test.cpp中
void func(int a)
{
	cout << a << endl;
}

//mian.cpp中
#include "Test.h"

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

image-20220426184451441

image-20220426184644402

声明不给,定义给

这种请情况会报错.

//Test.h文件中
#include <iostream>

using namespace std;

void func(int a);

//test.cpp中
void func(int a = 20)
{
	cout << a << endl;
}

//mian.cpp中
#include "Test.h"

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

image-20220426183959825

image-20220426184328625

  • 36
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 29
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 29
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玄鸟轩墨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值