目录
一.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 = #
}
但是显然,我们达不到这个目的。还是一样,用赋值的角度分析,我们传进去的是这个指针的 “拷贝本” ,修改的是拷贝本的指向,作用不到原来的指针,但如果是引用呢?
int num = 1;
void modifyPointB(int*& p)
{
p = #
}
这里传进去的可以理解为就是那个想修改的指针,所以自然可以修改成功。
还记得开头的时候说过 “不要理解为指针” 吗,因为有些地方可能当作指针可以理解,但是像上面这段 “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++,可能有解释得不对的地方,欢迎路过的大佬交流指正!!!