C++学习笔记(1)

目录

一.C++头文件及命名空间

1.头文件和源文件的命名方式

2.包含方式

(1)包含自己定义的头文件时

(2)包含标准库时

3.命名空间的引入

(1)创建命名空间

(2)命名空间的作用

(3)命名空间的访问

二.基本输出输入

1.输出

2.输入

3.字符串输入的空格问题

三.新数据类型

1.bool类型

2.空指针nullptr 

3.引用类型

(1)基本引用

(2)常引用

4.引用的应用场景

(1)函数传参(可防止拷贝产生)

(2)函数返回值(增加左值的使用)

5.auto类型(自动推断类型)

(1)如何用auto定义变量

(2)auto 用途广泛

四.函数思想

1.内敛思想(inline关键字)

2.函数重载

3.函数缺省


一.C++头文件及命名空间

1.头文件和源文件的命名方式

用.h命名头文件,用.cpp命名源文件

2.包含方式

(1)包含自己定义的头文件时

此时,跟C语言是一样的,例如我们写了一个叫“Tool”的头文件,我们可以:#include "Tool.h"这么写

(2)包含标准库时

如果是包含C语言的标准库,可以按照C语言的写法,例如:#include <stdio.h> 也可以去掉.h前面加c,就像:#include <cstdio>。

3.命名空间的引入

(1)创建命名空间

namespace 空间名
{

}

(2)命名空间的作用

在C语言中,同一作用域下不能使用相同的标识符,例如你定义了一个变量a,那么在这个作用域中只能使用一个叫做a的变量。在C++中,命名空间的引入可以增加标识符的使用率,也就是说,在同一块作用域中,可以有两个都名为a的变量存在,只是这两个变量必须来自不同的命名空间。

namespace A
{
	int a = 0;
}
namespace B
{
	int a = 1;
}
int main()
{
	int b = A::a; //相当于把a赋值给了b 即b=0
	int c = B::a; //也是把a赋值给一个变量c,c=1
	//都是a赋值给一个变量,只是a来自不同的命名空间
}

(3)命名空间的访问

一.如何访问?————这里用到了作用域分辨符 “ :: ”。通过作用域分辨符可以访问对应空间里的函数,也可以访问对应空间里的变量。

namespace 空间名
{
	int a = 0;
	void print()
	{
		printf("hello");
	}
}
int main()
{
	空间名::a = 1001;
	空间名::print();
}

这里的 print()函数是在空间内实现的,C++支持在空间内声明,函数具体功能在空间外实现。这时,函数需要注明是哪个命名空间的函数,实现方式如下:

namespace 空间名
{
	int a = 0;
	void print();
}
void 空间名::print()
{
	printf("hello");
}

此外,作用域分辨符还能用来标识全局变量,当全局变量与局部变量的变量名相同时,可以通过“::”加以区分,因为在C语言中如果你在局部定义了一个跟全局变量变量名相同的变量,在局部定义的会默认覆盖全局的,无法区分,而C++可以,具体如下:

int x = 1;
namespace 空间名
{
	int x = 2;
	printf("%d", x); //这里打印出来的是局部变量,即x = 2
	printf("%d", ::x); //这里打印出来的是全局变量,即x = 1;

}

二.using语法

using namespace 空间名;

使用 using 以后,使用时就可以省略前缀,但它有作用域,要在此 using 语句之下才可省略,在上面的不行(同时要在同一个大括号里),具体如下:

namespace 空间名
{
	int x = 2;

}
using namespace 空间名;
int main()
{
	x = 2;
}

里面的 x 就是此空间名的 x ,但是这种做法可能会产生二义性问题,例如:

namespace A
{
	int x = 2;
}
namespace B
{
	int x = 1;
}
using namespace A;
using namespace B;
int main()
{
	x = 2;//这里的x不知道是谁的,因为A和B都有,VS会提示“x不明确”
}

空间名还能嵌套访问,简单来说就是三个字——剥洋葱!具体如下:

namespace A
{
	int a = 2;
	namespace B
	{
		int b = 1;
	}
}
int main()
{
	A::a = 1000;
	A::B::b = 1001;
	//也可以这样
	using namespace A::B;
	b = 1002;
}

二.基本输出输入

需要包含C++标准输入输出流的头文件 “iostream”,其中常用的命名空间是 “std”,一般这么写:

#include <iostream>
using namespace std;

1.输出

	cout << 输出内容;

也可以打印多个,具体实现如下:

#include <iostream>
using namespace std;
int main()
{
	int a = 1;
	char str[] = "I Love C++";
	cout << a << str <<"\n";
}

换行也可以用 “endl” 实现

#include <iostream>
using namespace std;
int main()
{
	cout << "\n";
    cout << endl;
	//都能实现换行
}

输出的时候也可以用强制类型转换,注意,C语言与C++格式有点不一样,但是C++里面能兼容C语言的格式。

int main()
{
	float a = 1.0f;
	cout << int(a); //C++的写法
    cout << (int)a; //C语言的写法
}

2.输入

	cin >> 输入的内容;

跟输出一样,也可以输入多个:

int main()
{
	int a;
	float b;
	cin >> a >> b;
}

3.字符串输入的空格问题

在C++中,默认以空格当作数据间隔。这里可能会不理解,我们来分析以下案例:

#include <iostream>
using namespace std;
int main()
{
	char str[10] = "";
	cin >> str;
	cout << str;
}

当我们输入字符串 “I Love C++”时,并没有出现我们想要的输出,反而是如下输出(第一行是输入,第二行是输出):

再来一个例子:

int main()
{
	char str[10] = "";
	cin >> str;
	cout << str;
	char c;
	cin >> c;
	cout << c;
}

 原本我们期望的是有两次输入输出,但是只有一次,这次我们还是输入了字符串 “I Love C++”,但结果仍旧不是我们所期望的,具体如下(第一行为输入,第二行为输出):

分析:问题一:为什么第一个案例中,只输出了一个 'I' ?

           问题二:为什么在第二个案例中没有第二次输入,而是直接输出?

问题一:

        还记得开头说过,在C++中,默认以空格当作数据间隔。所以我们输入的 “I Love C++” 中,“I” 后面是空格,所以 “I” 和后面的 “Love C++” 是不同的数据,不是同一个字符串,所以只有 “I” 存到了str里面,自然就只输出 “I”。

问题二:

        理解了问题一之后,再来看问题二,当第一轮输入完时,此时缓冲区里面还剩下 “Love C++” 这一段,所以第二个cin的时候,把缓冲区中剩下的那一段中第一个字符 “L” 存在了字符c中,因此没有第二段输入,只有输出。

所以,在我们知道问题的成因以后,可以用以前C语言的方法避免这种 “字符串或字符在输入前做了输入” 的问题。下面给出案例测试(第一三行为输入,第二四行为输出)。

int main()
{
	char str[10] = "";
	cin >> str;
	cout << str;
	while (getchar() != '\n');
	char c;
	cin >> c;
	cout << c;
}

 C++里也有解决方法,只是本人刚刚入门,还不太清楚,还需继续努力学习!

那我们怎么完成带空格的输入呢?可以用C++的函数,具体如下:

int main()
{
	char str[11] = "";
	cin.getline(str, 11);
	cout << str;
}

下面是输入输出:

可以看到这就正常啦!

三.新数据类型

1.bool类型

一.占用一个字节。

二.只要是非零表示成立(true),只有0或者指针为空时表示不成立(false)。

三.应用场景:一般充当返回值,充当开关作用。

四.可以用true和false赋值。

	bool a = true;
	a = false;

五.普通打印只能打印出0和1,但也可以以true和false来打印。

	bool a = true;
	cout << a <<endl;
	cout << boolalpha << a;

打印如下:

2.空指针nullptr 

	int* p = nullptr;

 C语言中用NULL来表示,C++两者均可,但是NULL也可表示0,在某些情况(像函数传参)的时候可能会出现问题,nullptr 可以说是 “专门的人干专门的事”。

3.引用类型

这里把引用类型理解为 “起别名” 会更好理解,不要理解为指针。

(1)基本引用

	类型& 别名 = 要起别名的东西;
	//例如
	int a = 1;
	int& b = a;

注意:基本引用只能给已经定义的东西起别名,且不能轻易给常量区别名。

​
	int& a = 1;//这样编译器会报错

于是我们可以这样做:

	const int& a = 1;

这样编译器就不会报错了,其实像这种 “右值” 的引用方式,C++有专门的方式,也就是接下来要介绍的,至于跟上面这种有何区别,先按下不表,后面会作分析。

(2)常引用

常引用,也叫做 “右值引用”。

	类型&& 别名 = 要起别名的右值;
	int&& a = 1;

既然称作右值引用,那么只能给右值起别名,普通变量会报错。

	int a = 1;
	int&& b = a; //编译报错

4.引用的应用场景

(1)函数传参(可防止拷贝产生)

C语言中所有的函数传参都可理解为赋值,来举一些例子:

void modifyA(int a)
{
	a = 1001;
}

通过学习C语言知道,调用这个函数并不能修改传入的实参的值,其实是因为传入的不是这个值,是这个值的 “拷贝本” ,在函数里修改的也只是它的拷贝本,无法真正作用到实际的参数上面,其实用指针修改也是这个道理,都是传 “它的拷贝本” ,拷贝进一个地址,然后在函数里修改这个地址对应的值,以达到目的。

那引用呢?我们来看下面的例子:

void modifyB(int& a)
{
	a = 1001;
}

这个时候传进去的,是一个参数的别名,而别名就是它本身,所以可以理解为就是传了个实参。

这里再举一个例子会更容易理解,下面这个函数的意图是想实现 “修改指针指向”:

int num = 1;
void modifyPointA(int* p)
{
	p = &num;
}

但是显然,我们达不到这个目的。还是一样,用赋值的角度分析,我们传进去的是这个指针的 “拷贝本” ,修改的是拷贝本的指向,作用不到原来的指针,但如果是引用呢?

int num = 1;
void modifyPointB(int*& p)
{
	p = &num;
}

这里传进去的可以理解为就是那个想修改的指针,所以自然可以修改成功。

还记得开头的时候说过 “不要理解为指针” 吗,因为有些地方可能当作指针可以理解,但是像上面这段 “int*& p” ,如果理解为指针,'*'跟'&'不就抵消了吗?这还怎么理解?

再来填一个前面埋下的坑,const int& a 跟 int&& a 的区别。上代码!

void modifyA(int&& a)
{
	a += 11;//可以修改
	cout << a;
}
void modifyB(const int& a)
{
	a += 11;//它会报错,并提示你“表达式必须是可修改的左值”
	cout << a;
}

他们的区别体现在充当函数参数上,可以看到传进的是常引用时可以修改,不会报错。但修改是真的改了原本这个常量吗,其实不是的,这里可以理解为在函数中提供了修改右值的接口,修改作用并不会改变原本a的值,比如我们把1传进去,在这个函数里面可能被改成12,但出了这个函数它还是它,1还是1。在第二个函数里,const 就是只读的意思,自然不能修改,就会报错。

(2)函数返回值(增加左值的使用)

什么意思呢?先看代码!

int num = 1;
int getData()
{
	return num;
}
int main()
{
	getData() = 1001;//会报错,它会告诉你“表达式必须是可修改的左值”
	return 0;
}

​

可以看出,以往的函数返回值都是 “右值” ,即 “都是确定的值” 。

但是返回的引用却是一个可修改的左值!

int num = 1;
int& getValue()
{
	return num;
}
int main()
{
	getValue() = 1001;
	cout << num;
	return 0;
}

打印结果如下:

可以看到,不仅可以修改,而且修改的结果实实在在地作用到了num上面,因此我们可以这样理解:返回引用等效于返回变量本身! 

5.auto类型(自动推断类型)

(1)如何用auto定义变量

	auto a;//不能这么定义,会报错
	auto b = 1;//b被推断为整数类型

auto 会根据后面赋值的类型来推断要定义的这个变量是什么类型,十分方便。

(2)auto 用途广泛

下面举一个例子,可以更加深刻体会其方便之处:

int Fun1(int a, int b)
{
	//任意一个函数
}
void Fun2(int(*Fun1)(int,int),int a,int b)
{
	//任意一个想把Fun1当作参数传递进来的函数
}

现在我们想要定义一个指向Fun2的函数指针,用C语言的方法,是这样的:

	void(*p)(int(*)(int, int), int, int) = Fun2;

用C++的auto是这样的:

	auto p = Fun2;

可以看出,使用auto给我们带来了很多便利!

四.函数思想

1.内敛思想(inline关键字)

什么函数可以叫做它很内敛?

这个函数编译完成的代码直接以二进制的形式存在,就能称它内敛。内敛的函数是 “以空间换时间”,运行速度很快。类和结构体中定义的函数默认是内敛的。一般用得比较多且短小精悍的函数可以把它定义成内敛函数。定义如下:

inline void Fun1()
{
	//任意一个函数
}

2.函数重载

在C++中,允许同名不同参的函数存在,在C语言中同名是不行的。

这样的函数表现为:

(1)参数数目不同;

(2)参数类型不同;

(3)参数顺序不同;(建立在不同类型的基础上)

int print(int a, int b)
{

}
int print(char a, int b)
{

}
int print(int a, char b)
{

}

调用重载函数时,优先调用类型一致的函数。

3.函数缺省

在C++中,允许给函数形参初始化,但顺序必须从右往左:

int Fun1(int a = 1, int b)//会报错
{

}
int Fun2(int a = 1, int b = 2)//不会报错
{

}

怎么理解函数缺省?可以把它理解为重载的综合性写法。

int Fun(int a = 1, int b = 2, int c = 3)
{

}
int main()
{
	Fun();        //a = 1,b = 2,c = 3
	Fun(10);      //a = 10,b = 2,c = 3
	Fun(10, 20);  //a = 10,b = 20,c = 3
	Fun(10.20,30);//a = 10,b = 20,c = 30
}

传进去的值会覆盖初始化的值,没有传进去则不变。

初学C++,可能有解释得不对的地方,欢迎路过的大佬交流指正!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值