一些C++入门基础
关键字

命名空间
命名空间解决了C没办法解决的各类命名冲突问题
C++的标准命名空间:std
命名空间中可以定义变量、函数、类型:
namespace CS {//变量char cs408[] = "DS,OS,JW,JZ";int cs = 408;//函数void Func() {cout << "085400" << endl;}//类class student {public:void F() {cout << "bull and horse" << endl;}private:int a = 6;int b = 66;};
}
且命名空间可以嵌套(不常用):
namespace School {int x = 0;namespace CS {//};};
授权
命名空间未展开时,默认不能访问命名空间
全部展开:
using namespace CS;
部分展开(仅可访问部分已展开的变量、函数、类):
using CS::cs408;
或者加上作用域限定符直接访问:
CS::student st1;st1.F();
合并
如果定义了多个同名的命名空间,会自动合并:
如果合并后有多个同名变量、函数、类型,会出现重定义。
缺省参数
声明或者定义函数时,为参数指定的缺省值,如没有传参时,使用缺省值:
Date(int year = 1, int month = 1, int day = 1) {};
全缺省:
Date(int year = 1, int month = 1, int day = 1) {};
半缺省(部分缺省):
半缺省参数必须从右往左给出,且不能间隔着给。
所以,调用半缺省参数的函数时,传参必须从左往右给出,同样不能间隔。
Date(int year, int month = 1, int day = 1) {};Date(2025);
Date(int year, int month, int day = 1) {};Date(2025,5);
注:在函数声明和定义分离的情况下,不能为声明和定义同时给出半缺省参数,避免声明和定义中的缺省值不同而无法调用。
函数重载
C++允许同一作用域下存在同名函数,条件是:形参的个数 or 类型 or 顺序不同:
Date(int a);
Date(int a, int b);
Date(int a, char b);
Date(char a, int b);
原理
C/C++ 中,一个程序要运行需经历:预处理、编译、汇编、链接;
(每日回顾:C程序预处理(本文包括宏定义)-CSDN博客)
在汇编阶段,对于一个函数,汇编器会为其分配一个地址,并将该地址与函数名关联起来,记录在符号表中;在链接完成之后,会形成完整的符号表。符号表包含了程序中所有符号的名称、地址和属性等信息。

由上图可见,C语言仅在函数名前加 '_' 以在符号表中查找对应函数地址;而C++的函数名修饰中,还与形参有关,这也印证了前文所说函数重载的条件。
特殊情况
函数重载和缺省参数的函数同时存在可能会出现调用歧义:
//无参数 和 全缺省
void F() {};
void F(int a = 1, int b = 2) {};//一个参数 和 半缺省
void F(int a) {};
void F(int a, int b = 2) {};
引用
引用的底层和C指针一样,只是使用更加方便;一个变量的引用,就是给这个已存在的变量取个别名,这个引用与已存在的变量共用同一块内存空间。
int p = 0;
int& pp = p; // pp是p的引用pp = 1; // 修改pp,p也会随之修改
特性
1、必须在定义时就初始化;
2、一个变量可以有多个引用:
int p = 0;
int& pp = p;
int& ppp = p;
3、引用被初始化之后,不能再改变指向;
int p = 0;
int q = 1;
int& pp = p;
pp = q; // 报错,不能再改变指向
使用场景
引用作为参数
void test(int& a){};
int main(){int b = 1;test(b);return 0;
}
引用作为返回值
int& test(int& a){a++;return a;
};
int main(){int b = 1;int ret = test(b);return 0;
}
上面这段代码,其实test函数中返回的a变量,在出了函数作用域之后已经销毁,只不过我们用ret立刻接收了该引用的值;实际上返回的引用,已经是被释放的空间。
所以,如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。
效率
使用传值传参和使用值作为返回值类型时,实际上都是实参或者返回变量的一份临时拷贝;所以当值较大时,效率必然低于使用引用传参或者使用引用作为返回值类型。
和指针对比
引用的底层实现,与指针相同,只不过使用时更为方便。
1、引用 在概念上是一个变量的别名,而指针存储地址;
2、引用在定义时必须初始化指向实体,指针不必;
3、引用在初始化引用一个实体后,不能再改变指向,而指针可以;
4、sizeof(引用) 是引用类型的大小,而指针始终是地址所占字节数大小;
5、引用++ 是引用的实体++,而指针++ 是向后偏移;
6、没有多级引用,有多级指针;
7、访问实体方式不同:引用 编译器会处理,而指针需要显式解引用;
8、引用使用起来更安全。
内联函数
以inline修饰的函数为内联函数。
普通函数在调用时,需要建立栈帧;内联函数会在编译时直接展开,没有建立栈帧的开销,提升程序运行效率。
有点像C中的宏函数,只不过宏是完全的只有替换,而内联函数是展开函数体。
inline void test(int a){};
特性
1、以空间换时间,但如果函数体过大,会使文件变大;
2、不同编译器的实现不同,一般编译器会将函数规模较小(10行左右)、且不是递归、且频繁调用的函数,采用inline修饰,否则会忽略。(内联说明只是向编译器发出的一个请求,编译器可以选择忽略这个请求)
3、inline函数不建议声明和定义分离。因为内联函数展开后,就没有函数地址了,只有声明就会找不到定义,报链接错误。
auto关键字(C++11)
auto作为一 个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。简单来说,auto可以推导当前变量类型。
int a = 0;
auto b = a;
auto c = 'a';
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto 的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为变量实际的类型。
你不能使用auto来推导未知类型。
auto常用来与范围for、lambda表达式搭配使用。
范围for(C++11)
范围for使得在遍历一个有范围的集合时,更加方便:
int arr[10] = {0};
for (auto a : arr){cout << a << endl;
}
这段代码会依次取数组中所有的数据赋值给 a ,打印;
使用条件
1、for 循环迭代的范围是确定的(下图展示了不确定的范围);
void test(int arr[]){for(auto a : arr){cout << a << endl;}
}
2、迭代的对象要实现++和==的操作。
nullptr
int* p1 = NULL;
int* p2 = 0;
在C的头文件 stddef.h 中,有部分代码:
(条件编译见每日回顾:C程序预处理(本文包括宏定义)-CSDN博客)
#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif
NULL实际是一个宏,可能被定义为字面常量0,或者被定义为无类型指针(void*)的常量。
但是如果有:
void test(int a)
void test(int* a){};
int main(){test(NULL);
}
那么test(NULL) 到底调用谁?这就出现了问题,C++11中通过引入关键字nullptr 用来初始化空指针。