C++ 基础概念
C++是兼容C的,所以我们可以在C++环境下写C语言。
C++之父:本贾尼・斯特劳斯特卢普。
一、命名空间
{
rand = ;
(, rand);
}
C++ 的基础概念,涵盖命名空间及其嵌套解决标识符冲突的方法,C++ 输入输出流 iostream 及 cin/cout 的使用,缺省参数的定义与规则,函数重载的条件与示例,引用的概念、语法、要求及与指针的区别,const 引用对临时变量和常量的修饰,宏的定义与内联函数的对比,以及 C++11 中 nullptr 关键字的引入原因。文章通过代码示例详细讲解了各知识点的具体用法和注意事项。

C++是兼容C的,所以我们可以在C++环境下写C语言。
C++之父:本贾尼・斯特劳斯特卢普。
{
rand = ;
(, rand);
}
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main() {
printf("%d\n", rand); // 会编译报错
}
思考:stdlib.h中存在 rand 这个函数,那么我们却自定义了 rand 这个变量。当 rand 是局部变量时,代码可以成功执行,当 rand 是全局变量时却会引起编译报错,这是什么原因呢?
下面我们需要了解 C++ 的标识符查找规则和引起标识符命名冲突的本质原因。
标识符:变量、函数、类、常量、命名空间。
编译器查找标识符时,会从当前最内层的作用域开始查找;如果找到了,就不会再去外层作用域查找。
同一作用域下不能存在同名的标识符。
那么用怎么样才能避免编译报错呢?
这里就引入了命名空间的概念。
命名空间就是我自己造了一个域,用来存放标识符。用来解决同一个域下命名冲突的问题。
用(命名空间名::标识符)来访问我域里的标识符。
语法:
namespace 命名空间名 {
// 你自定义的标识符
}
#include <stdio.h>
#include <stdlib.h>
namespace mycode{
int rand = 10;
}
int main() {
printf("%d\n", mycode::rand); // 正常打印出 10
}
那么在同一个命名空间下,标识符一样该怎么处理,这里我们就需要了解命名空间的嵌套。
在父域里辟多个子域。用来解决父域中标识符冲突问题。用(父域命名空间名::子域 1::标识符)来访问我域里的标识符。
namespace 外层命名空间名 {
namespace 内层命名空间名 1 {
// 你自定义的标识符
}
namespace 内层命名空间名 2 {
// 你自定义的标识符
}
//......
}
#include <stdio.h>
namespace code{
namespace codeA {
int a = 10;
}
namespace codeB {
int a = 20;
}
}
int main() {
printf("%d\n", code::codeA::a); // 打印 10
printf("%d\n", code::codeB::a); // 打印 20
}
就是我们前面用的 ::
就是访问命名空间里的某个成员。
语法:
using 命名空间名::成员;
示例:
#include <stdio.h>
namespace name {
int a = 3;
int b = 6;
}
int main() {
using name::a; // 展开某个成员
printf("%d\n", a);
}
语法:
using namespace 命名空间名;
示例:
#include <stdio.h>
namespace name {
int a = 3;
int b = 6;
}
int main() {
using namespace name; // 展开全部成员
printf("%d\n", a);
printf("%d\n", b);
}
在多个文件使用命名空间时
C++ 不允许同一个作用域下有两个同名的类或变量,但允许同一个命名空间被写在不同的文件、不同的位置,编译器会自动把它们合并成一个。
C++标准库都存放在一个名叫 std(standard) 的命名空间中。
是 Input Output Stream 的缩写,为你提供了输入输出 (cin、cout) 和操作规则 (<<、>> )。
std::cin是在控制台输入的意思,我们知道 C++ 的标准库都放在 std 这个命名空间里。所以前面要加上 std::,这里面的 c 是 console(控制台) 的意思,in 是 input(输入的意思)。
std::cout与上面唯一的区别是 out 是输出的意思。所以表示的是在控制台输出的意思。
<<是流插入运算符,和 cout 搭配使用。例如 std::cout<<"hello world"。把 hello world 在终端打印出来。可以一次打印多个内容。
#include <iostream>
int main() {
using namespace std;
cout << "hello world" << endl;
cout << "我的刀盾喜欢奶龙" << endl << "咕咕嘎嘎" << endl; // 在屏幕上打印两行内容
}
是流提取运算符,和 cin 搭配使用。也可以一次输入多个内容。
#include <iostream>
int main() {
using namespace std;
int a, b;
cout << "请输入两个整数:";
cin >> a >> b;
cout << "两个数之和为:" << a + b << endl;
}
需要注意的 C++ 可以自动识别变量类型,不需要我们手动输入占位符。
endl 表示换行的意思。
定义函数时,给定参数一个默认值 (这个值也叫做缺省值)。在传参数时,如果不传参数就使用默认值,传参数就使用你传的参数。
#include <iostream>
using namespace std;
void Print(int a= 3) {
cout << a << endl;
}
int main() {
Print(); // 打印 3
Print(6); // 打印 6
}
给函数的全部参数设置缺省值。
#include <iostream>
using namespace std;
void Print(int a= 3,int b = 2) {
cout << a << "," << b << endl;
}
int main() {
Print(); // 打印 3,2
Print(0,0); // 打印 0,0
}
给函数的部分参数设置缺省值。半缺省中,C++规定缺省值必须从右向左连续传。在使用时,必须传够没用缺省值的参数,且是按照从左向右传的顺序。
#include <iostream>
using namespace std;
void Print(int a, int b = 1, int c = 2) {
cout << a << "," << b << ","<< c << endl;
}
int main() {
//Print(); // 没有传够参数,会触发编译错误
Print(0); // 打印 0,1,2 半缺省起码传一个
Print(6,6); // 打印 6,6,2
Print(5,5,5); // 打印 5,5,5
}
函数声明和定义分离时,函数的缺省参数只能出现一次,且出现在声明部分。
#include <iostream>
using namespace std;
void Print(int a, int b = 3); // 函数声明部分 (缺省值必须写在声明里)
void Print(int a , int b) { // 函数定义部分
cout << a << "," << b << endl;
}
int main() {
Print(3); // 打印 3,3
Print(2,6); // 打印 2,6
}
C++规定在同一作用域内,可以存在一个或多个同名函数。但是这个同名函数需要满足参数个数不同 || 参数类型不同 || 参数顺序不同 (参数类型的顺序不同)。
#include <iostream>
using namespace std;
int Add(int a, int b, int c) {//参数个数不同
return a + b + c;
}
int Add(int a, int b) {
return a + b;
}
int main() {
int a = Add(1,1,1);
int b = Add(1, 1);
cout << a << endl << b << endl;
}
#include <iostream>
using namespace std;
int Add(int x,int y) {//参数类型不同
return x + y;
}
double Add(double x, double y) {
return x + y;
}
int main() {
int a = Add(2, 3);
double b = Add(1.2, 1.3);
cout << a << endl << b << endl;
}
#include <iostream>
using namespace std;//参数类型不同
double Calc(int a,double b) {
return a + b;
}
double Calc(double a,int b) {
return a - b;
}
int main() {
double a = Calc(2, 2.2);
double b = Calc(2.2, 2);
cout << a << endl;//a 的参数第一个是整型,第二个是浮点型,所以调的是加法函数 (4.2)
cout << b << endl;//b 的参数第一个是浮点型,第二个是整型,所以调的是减法函数 (0.2)
}
给已有的变量起一个别名。直接在原内存上操作这个变量。
类型& 引用别名 = 引用对象;
以我们以前常写的 Swap 函数为例,我们来体会引用的妙处。
#include <iostream>
using namespace std;
void Swap(int* a, int* b) {//用指针的写法
int tmp = *a;
*a = *b;
*b = tmp;
}
int main() {
int a = 2, b = 3;
cout << "交换前" << "a = " << a << " " << "b = " << b << endl;
Swap(&a, &b);
cout <<"交换后" << "a = " << a << " " << "b = " << b << endl;
}
#include <iostream>
using namespace std;
void Swap(int& a, int& b) {//用引用的写法
int tmp = a;
a = b;
b = tmp;
}
int main() {
int a = 2, b = 3;
cout << "交换前" << "a = " << a << " " << "b = " << b << endl;
Swap(a, b);
cout << "交换后" << "a = " << a << " " << "b = " << b << endl;
}
对于局部/全局使用引用时必须进行初始化。比如顺序表的初始化,在 main 函数内部使用引用。
#include <iostream>
using namespace std;
int main() {
int a = 10;
int& b = a; // 引用时必须进行初始化
cout << &a << endl;
cout << &b << endl; // 在这里可以看到 a,b 是同一个地址
}
对于函数形参里的引用,你传的实参就是已经完成了初始化。比如前面的 Swap 函数。你传完参数,其实就完成了对形参的初始化。
对于不同作用域下引用的别名可以相同,比如我们上面的 Swap 函数。同一作用域下引用的别名不能与原变量的名字相同。
一个变量可以有多个引用,一个引用的改变一定会改变原变量和其它引用的值。
#include <iostream>
using namespace std;
int main() {
int a = 2;
int& b = a;
int& c = a; // 这里可以写 a 也可以写 b
cout << "变化前" << a << ","<<b << "," << c << endl;
c++;
cout << "变化后" << a << "," << b << "," << c << endl; // 这里可以观察到
//a,b,c 的值均发生了变化
}
引用一旦绑定某个实体 (有内存地址,能存储数据的对象;例如变量、数组),就终身只是这个实体的别名。再次对引用的操作,只是在修改引用的值,内存永远都是那一块。
int main() {
int a = 2;
int b = 3;
int& ra = a;
ra = b; // 只是赋值操作
//int& ra = b; // 重定义错误
}
引用在实践中往往用于函数传参或者做返回值。因为它直接对内存进行操作,就减少了拷贝从而提高了时间效率。
被 const 修饰的引用。原数据进行 const 引用后权限只能不变或者变小。
原变量权限为只读,const 引用后权限还是只读。
int main() {
const int a = 3; // 权限为只读,不能修改 a = 3 的值
const int& ra = a; // 权限仍然为只读,不能修改 ra = 3 的值
//int& ra = a; // 错误写法,权限不能被放大
}
原变量为可读可写,const 引用后权限为只读。
int main() {
int a = 3; // 可读可写
const int& ra = a; // 只读
a = 6; // 正确写法,没有超出权限
//ra = 3; // 错误写法,超出了权限
}
变量是指内存空间和存储的值。
临时变量是指为了保证代码能跑,编译器自己制定的一份临时空间。生命周期随着这条语句的执行结束立马销毁。
常见的有单独的常量 (表达式算出来没有用变量存储的也算),不同类型转化时触发的临时变量的创建。
对于普通的引用只能绑定有内存地址的变量。核心原因是普通的引用可读可写,对于临时变量这种临时产生的对象,本身就不安全。你还提升访问权限,这不就太过分了。
而 const 引用可以修饰临时变量的主要原因就是你临时变量虽然本身就不安全。反正我只读,都会把你改为安全的,反正不会对你进行操作。唯一的变化就是延长了临时变量的生命周期,const 引用什么时候销毁,临时变量什么时候销毁。
int main() {
const int& ra = 20; // 安全
int& rra = 20; // 不安全,属于权限放大
}
int main() {
int a = 20;
const int& ra = a * 3; // 安全
int& rra = a * 3; // 不安全,权限放大
}
int main() {
int a = 3;
const double& ra = a; // 安全
double& rra = a; // 不安全,权限放大
}
①语法:引用只是给变量取一个别名,不需要单独再开空间。而指针是存储地址的变量,所以需要单独开空间。
②初始化:引用在定义时必须进行初始化;而指针一般建议初始化,避免野指针。
③特性:引用在绑定一个对象后,就不能再改变;而指针绑定一个对象后可以重新绑定。
④访问:引用可以直接访问对象;而指针需要解引用才能访问对象。
⑤大小:引用的大小取决于对象的大小;指针的大小取决于操作系统的位数(32 位下占 4 个字节;64 位下占八个字节)
⑥安全性:指针很容易出现空指针和野指针;而引用不存在这个问题。
宏是在预处理阶段,给一段文本 (值/表达式/代码) 绑定一个宏名(符号),分为有参宏和无参宏。
语法:
#define 宏名 替换文本
例子:
#define val 18
int main() {
using namespace std;
cout << val << endl; // 在执行程序时编译器会自动将 val 换成 18
}
语法:
#define 宏名 (参数 1,参数 2,……) 替换文本//括号里是参数列表
例子:
#define Add(a,b) ((a) + (b))
int main() {
using namespace std;
cout << Add(2, 3) << endl; // 输出 5
}
注意:这里 a 和 b 加括号,原因是为了避免运算符优先级带来的计算错误。
被 inline 修饰的函数叫做内联函数。inline 的作用是将代码在调用处展开,不需要再建立栈帧了,提高了效率。
inline 只是建议,编译器可以采纳也可以不采纳。一般内联函数都是几行的小代码。
#include <iostream>
using namespace std;
inline int Add(int a,int b) {
return a + b;
}
int main() {
cout << Add(2, 3) << endl; // 就是这个意思 cout << 2+3 << endl;
}
①本质:内联函数是函数;宏是文本替换
②运算符优先级:内联函数不存在问题;宏需要手动添加括号
③调试:内联函数可以调试,宏不能调试
④编译器:编译器可以拒绝内联;编译器不能拒绝宏,宏是强制替换
nullptr 是 C++11 引入的一个关键字,用来表示空指针。
NULL 在 C++表示的是 0 的意思,如果在函数重载时,就会发生匹配歧义。
#include <iostream>
using namespace std;
void Print(int* x) {
cout << "int*" << endl;
}
void Print(int x) {
cout << "int" << endl;
}
int main() {
Print(NULL); // 你预期的结果是 int,可是运行结果却是 int*
Print(nullptr); // 符合预期结果
}

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online