Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~
💥💥个人主页:奋斗的小羊
💥💥所属专栏:C++
🚀本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为记录我的学习过程及理解。文笔、排版拙劣,望见谅。
目录
- 前言
- 一、C++基础知识
-
- 1、C++关键字(C++98)
- 2、命名空间
- 3、C++输入&输出
- 4、缺省参数
- 5、函数重载
- 6、引用
-
- 6.1引用概念
- 6.2引用的特性
- [6.3 引用的使用](#6.3 引用的使用)
- [6.4 const引用](#6.4 const引用)
- 6.5指针和引用的关系
- 7、inline
- 8、nullptr
- 总结
前言
C语言是结构化和模块化的编程语言,适合处理较小规模的程序,对于复杂、规模较大的问题,需要高度的抽象和建模时,C++更加合适。
C++兼容C语言绝多数的语法,C++是在C语言的基础上,容纳进去了面向对象编程思想,并增加了许多有用的库,以及编程范式等。
C语言有一些语法的不足,C++对C语言设计不合理的地方进行了优化,C++既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计。
一、C++基础知识
1、C++关键字(C++98)
C语言有32个关键字,而C++有63个。
分类 | 关键字 |
---|---|
控制语句相关 | if , else , switch , case , default , break , continue , goto , do , while , for , return |
数据类型相关 | int , char , float , double , void , bool , wchar_t , short , long , unsigned , signed |
存储类 | auto , register , static , extern , mutable , thread_local |
类型相关 | typedef , using , decltype , sizeof , alignas , alignof |
类和对象相关 | class , struct , union , enum , explicit , friend , this , virtual , override , final , public , protected , private , new , delete , const , volatile , constexpr , static_assert |
模板相关 | template , typename , class (在模板定义中) |
命名空间 | namespace , using |
异常处理 | try , catch , throw |
其他 | sizeof , nullptr , alignas , alignof |
预处理指令相关 | #define , #include , #if , #endif , #ifdef , #ifndef , #else , #elif , #pragma |
2、命名空间
2.1命名空间的意义
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,难免会导致冲突。
使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染 ,namespace
关键字的出现就是针对这种问题的。
例如:
c
#include <stdio.h>
int rand = 10;
int main()
{
printf("%d\n", rand);
return 0;
}
上面定义了一个全局变量rand
,编译运行打印出rand
的值。
而当我们包含上头文件<stdlib.h>
编译运行就会出现问题:
c
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{
printf("%d\n", rand);
return 0;
}
像上面这种情况编译运行就会报错,因为rand
是定义在头文件<stdlib.h>
中的一个函数,命名冲突了。
C语言没办法解决这种命名冲突的问题,所以C++提出了namespace
来解决。
2.2命名空间的定义
定义命名空间,需要使用到namespace
关键字,后面跟命名空间的名字 ,然后接一对{}
即可,{}
中为命名空间的成员。
命名空间域都是定义在全局的。
例如:
cpp
namespace yjz
{
//命名空间中可以定义变量、函数、类型
int rand = 10;
int Add(int x, int y)
{
return x + y;
}
struct S
{
int n;
char c;
double d;
};
}
yjz
是我名字的缩写,一般开发中用项目名命名。
上面是常规的命名空间定义,命名空间还可以嵌套:
cpp
namespace yjz
{
//命名空间中可以定义变量、函数、类型
int rand = 10;
int Add(int x, int y)
{
return x + y;
}
struct S
{
int n;
char c;
double d;
};
namespace jzy
{
int m;
int Sub(int x, int y)
{
return x - y;
}
}
}
namespace
本质是定义出一个域,这个域跟全局域各自独立,不同的域可以定义同名变量。C++有函数局部域、全局域、命名空间域、类域四个域。
同一个工程中(不同的
.h
和.cpp
文件中)允许存在多个相同名称的命名空间,编译器最后会合并成一个。
| 注意: 一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中。
2.3命名空间的使用
编译查找一个变量的声明/定义时,默认只会在全局或局部查找,不会到命名空间里面查找。使用命名空间有三种方式:
- 加命名空间名称及作用域限定符
cpp
int main()
{
printf("%d\n", yjz::rand);
return 0;
}
- 使用
using
将命名空间中某个成员引入
cpp
using yjz::jzy::m;
int main()
{
printf("%d\n", yjz::rand);
printf("%d\n", m);
return 0;
}
- 使用
using namespace
命名空间名称引入
cpp
using namespace yjz;
int main()
{
printf("%d\n", yjz::rand);
printf("%d\n", jzy::m);
printf("%d\n", Add(3, 5));
return 0;
}
3、C++输入&输出
下面是我们学的第一条C语言代码:
c
#include <stdio.h>
int main()
{
printf("Hello world!\n");
return 0;
}
而同样的功能,C++是这样实现的:
cpp
#include <iostream>
using namespace std;
int main()
{
cout << "Hello world!" << endl;
return 0;
}
std
是C++标准库的命名空间名,C++将标准库的定义实现都放到这个命名空间中。
其中:
- 使用
cout
标准输出对象(控制台)和cin
标准输入对象(键盘)时,必须包含<iostream>
头文件以及按命名空间使用方法使用std
cout
和cin
是全局 的流对象,endl
其实是一个函数,是特殊的C++符号,表示换行输出,它们都包含在<iostream>
头文件中<<
是流插入运算符,>>
是流提取运算符- C++输入输出更方便,可以自动识别变量类型,不需要像C语言一样手动控制格式
cout
和cin
分别是ostream
和istream
类型的对象,<<
和>>
也涉及运算符重载等,后续会详细介绍
| 输出:
cpp
int main()
{
int n = 1;
char c = 'a';
double d = 3.14;
cout << n << " " << c << " " << d << endl;
return 0;
}
| 输入:
cpp
int main()
{
int n = 1;
char c = 'a';
double d = 3.14;
cin >> n;
cin >> c >> d;
cout << n << endl;
cout << c << " " << d << endl;
return 0;
}
这里要特别注意的是:输入操作变量名前不加
'&'
符号,这是不同于C语言的地方。
| std命名空间的使用惯例:
std
是C++标准库的命名空间,如何展开std
使用更合理?
- 在日常练习中,使用
using namespace std
即可,方便 using namespace std
展开,标准库就全部暴露出来了,如果我们定义和库重名的类型、对象、函数,就会产生冲突。我们日常练习代码量小,基本不会出现这样的问题,但项目中代码量大,这样做很容易出现问题- 在项目开发中推荐使用
std::cout
这样指定命名空间,或者using std::cout
展开常用的库对象/类型等方式
4、缺省参数
缺省参数是声明或定义函数时为函数的参数指定一个缺省值,在调用函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。
cpp
void Func(int x = 5)
{
cout << x << endl;
}
int main()
{
Func();//没有传参时使用参数的默认值
Func(10);//传参时使用指定的实参
return 0;
}
缺省参数分为全缺省和半缺省。
- 全缺省参数
cpp
void Func(int x = 1, int y = 2, int z = 3)
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
cout << "z = " << z << endl;
}
- 半缺省参数
cpp
void Func(int x, int y, int z = 3)
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
cout << "z = " << z << endl;
}
| 注意:
- 半缺省参数必须从右往左依次给出,不能间隔着给
- 缺省参数不能在函数声明和定义中同时出现
- 缺省值必须是常量或全局变量
下面都是错误示范:
cpp
void Func(int x = 1, int y = 2, int z)
{
cout << "x = " << x << endl;
cout << "y = " << y << endl;
cout << "z = " << z << endl;
}
cpp
//test.h
void Func(int x = 1);
//test.c
void Func(int x = 2)
{
//
}
函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现 ,规定必须函数声明给缺省值。
5、函数重载
C++支持在同一作用域中出现同名函数 ,但是要求这些同名函数的形参不同,可以是参数个数 不同或者类型 不同,这样C++函数就表现出了多态行为,使用更加灵活。
例如:
cpp
int Add(int x, int y)
{
return x + y;
}
double Add(double x, double y)
{
return x + y;
}
int main()
{
cout << Add(1, 1) << endl;
cout << Add(2.2, 2.2) << endl;
return 0;
}
提高了代码可读性,通过使用相同的函数名称来执行相似的操作,可以使代码更加简洁和易于理解。
6、引用
6.1引用概念
引用不是新定义一个变量,而是给已有的变量取一个别名 ,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
类型& 引用别名= 引用对象;
cpp
int main()
{
int a = 10;
int& b = a;//给a取个别名叫b
a++;
b++;
cout << &a << endl;
cout << &b << endl;
cout << a << endl;
cout << b << endl;
return 0;
}
注意: 引用类型必须和引用实体是同类型的。
6.2引用的特性
- 引用在定义时必须初始化
- 一个变量可以有多个引用
- 引用一旦引用一个实体,再不能引用其他实体
6.3 引用的使用
- 函数调用过程中传参和返回值,没有了拷贝提高了效率
- 引用传参和指针传参是类似的,但是引用传参相对方便一些
- 引用和指针在实践中相辅相成,功能重叠,各有特点,互相不可替代
- C++引用定义后不能改变指向
例如:
cpp
void Swap(int& rx, int& ry)
{
int tmp = rx;
rx = ry;
ry = tmp;
}
int main()
{
int x = 1;
int y = 2;
cout << x << " " << y << endl;
Swap(x, y);
cout << x << " " << y << endl;
return 0;
}
6.4 const引用
- 可以引用一个
const
对象,但必须用const
引用,const
引用也可以引用普通对象,也就是对象的访问权限在引用过程中可以缩小,但不能放大
cpp
const int a = 10;
//int* ra = a;
const int& ra = a;
//权限可以缩小,但不能放大
int b = 10;
const int& rb = b;
- 表达式相加、函数调用传值返回、类型转换等会产生临时对象,C++规定临时对象具有常性
- 临时对象就是编译器需要一个空间暂存表达式的求职结果时临时创建的一个未命名的对象,C++把这个未命名对象叫做临时对象
6.5指针和引用的关系
- 语法概念上引用是给一个变量取别名不开空间,指针是存储一个变量的地址,要开空间
- 引用在定义时必须初始化,指针可以不初始化,但是不推荐
- 引用在初始化时引用一个对象后,就不能再引用其他对象,而指针可以改变指向对象
- 引用可以直接访问指向对象,指针需要解引用才能访问对象
sizeof
中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(4 / 8)- 指针容易出现空指针和野指针的问题,引用很少出现,所以引用使用起来相对安全一些
7、inline
- 用
inline
修饰的函数叫做内联函数,编译时C++编译器会在调用的地方展开内联函数,这样调用内联函数就不需要建立栈帧了,可以提高效率 inline
对于编译器而言只是一个建议,加了inline
编译器也可以选择在调用的地方不展开,不同编译器关于inline
什么情况展开各不相同,因为C++没有规定这个。inline
适用于频繁调用的短小函数,对于递归函数和代码相对对一些的函数,加上inline
也会被编译器忽略- C语言实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂容易出错,且不方便调试,C++设计了
inline
的目的就是替代C语言的宏函数 - VS编译器
debug
版本下默认是不展开inline
的,这样方便调试,debug
版本想展开需要另外设置 inline
不建议声明和定义分离到两个文件 ,分离会导致链接错误,因为inline
被展开就没有函数地址,链接时会报错,所以内敛函数建议直接放到.h文件
8、nullptr
NULL
实际是一个宏,C++中NULL
可能被定义为字面常量0,或者C中被定义为无类型指针(void*)
的常量,不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,本想通过f(NULL)
调用指针版本的f(int*)
函数,但是由于NULL
被定义成0,调用了f(int x)
,因此与程序的初衷相悖,f((void*)NULL)
调用会报错- C++11中引入
nullptr
,nullptr
是一个特殊的关键字,nullptr
是一种特殊类型的字面量,它可以转换成任意其他类型的指针类型,使用nullptr
定义空指针可以避免类型转换的问题,因为nullptr
只能被隐式地转换为指针类型,而不能被转换为整数类型
总结
- 作为在C语言基础之上发展出来的语言,C++对C语言设计不合理的地方做了很多优化,使得程序设计更加模块化,代码更容易组织和维护