作为一门兼顾性能与灵活性的编程语言,C++ 在编程领域始终占据重要地位。无论是大型系统开发、游戏引擎构建,还是嵌入式设备编程,都能看到它的身影。本文将从基础入门、核心特性、学习路径等方面,带你全面认识 C++,帮你快速搭建知识框架。
掌握 C++ 的核心基础特性,是实现从 "会写代码" 到 "写好代码" 的关键。文档中详细介绍了命名空间、输入输出、缺省参数、函数重载、引用、inline、nullptr 等基础概念,这些内容是 C++ 编程的 "基本功"。
一. 命名空间:解决命名冲突的核心工具
在 C/C++ 中,变量、函数、类若都存在于全局作用域,极易引发命名冲突(例如 C 语言中定义int rand = 10会与标准库中的rand()函数冲突)。C++ 引入namespace(命名空间),通过对标识符进行 "本地化",有效避免命名污染。
命名空间的使用需遵循以下规则:
定义方式 :使用namespace关键字 + 命名空间名 +{},内部可定义变量、函数、类型;支持嵌套定义,且多文件中同名的命名空间会自动合并(例如在 Stack.h 和 Queue.h 中都定义namespace bit,编译器会将其视为同一个命名空间);
使用方式 :共有三种方式,分别是 "指定命名空间访问"(如bit::rand,项目开发中推荐使用,避免冲突)、"using 展开单个成员"(如using bit::Add,适合频繁访问且无冲突的成员)、"using 展开全部成员"(如using namespace bit,仅适合小练习,项目中禁用,风险高)
cpp
#include <stdio.h>
#include <stdlib.h>
// 定义命名空间bit,包含变量rand和函数Add
namespace bit {
int rand = 10;
int Add(int left, int right) {
return left + right;
}
}
int main() {
// 访问全局作用域的rand()函数(输出函数地址)
printf("%p\n", rand);
// 访问bit命名空间的rand变量(输出10)
printf("%d\n", bit::rand);
// 访问bit命名空间的Add函数(输出3)
printf("%d\n", bit::Add(1, 2));
return 0;
}
二. 输入输出:C++ 风格的 IO 操作
C++ 提供了专属的输入输出库<iostream>,相比 C 语言的printf/scanf,具有 "自动识别变量类型""支持自定义类型" 等优势,使用更便捷。
核心组件与用法如下:
std::cin :istream 类的对象,用于标准输入,通过>>(流提取运算符)读取数据;
std::cout :ostream 类的对象,用于标准输出,通过<<(流插入运算符)输出数据;
std::endl:插入换行符并刷新缓冲区;
效率优化 :在大量输入场景(如竞赛题)中,可添加ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);关闭同步、解绑流,提升 IO 效率
cpp
#include <iostream>
using namespace std;
int main() {
int a = 0;
double b = 0.1;
char c = 'x';
// 输出:0 0.1 x(自动识别类型)
cout << a << " " << b << " " << c << endl;
// 输入:从键盘读取a和b的值
cin >> a >> b;
// 输出更新后的值
cout << "a: " << a << ", b: " << b << endl;
return 0;
}
三. 缺省参数:提升函数灵活性
缺省参数(又称默认参数)是指在声明或定义函数时,为参数指定默认值。调用函数时,若未传入实参,则使用默认值;若传入实参,则使用指定值,极大提升了函数的灵活性。
使用缺省参数需遵循以下规则:
分类 :分为 "全缺省"(所有参数都有默认值)和 "半缺省"(部分参数有默认值),其中半缺省参数必须从右往左连续指定(例如void Func(int a, int b=10, int c=20)合法,void Func(int a=10, int b, int c=20)非法);
调用规则 :必须从左到右依次传入实参,不能跳跃传参(例如调用Func(100, 200)对应a=100, b=200, c=20,合法;调用Func(, 200, 300)非法);
声明与定义:若函数声明与定义分离,缺省参数只能在声明中指定(不能同时在声明和定义中出现,避免歧义)
cpp
#include <iostream>
using namespace std;
// 全缺省函数声明(所有参数有默认值)
void Func1(int a = 10, int b = 20, int c = 30);
// 半缺省函数声明(从右往左连续指定默认值)
void Func2(int a, int b = 10, int c = 20);
// 函数定义(无缺省参数)
void Func1(int a, int b, int c) {
cout << "a: " << a << ", b: " << b << ", c: " << c << endl;
}
void Func2(int a, int b, int c) {
cout << "a: " << a << ", b: " << b << ", c: " << c << endl;
}
int main() {
Func1(); // 输出a:10, b:20, c:30(使用全默认值)
Func1(1, 2); // 输出a:1, b:2, c:30(传入前两个实参)
Func2(100); // 输出a:100, b:10, c:20(传入第一个实参)
Func2(100, 200); // 输出a:100, b:200, c:20(传入前两个实参)
return 0;
}
四. 函数重载:同名函数的灵活使用
C++ 支持在同一作用域中定义同名函数,即函数重载,但要求同名函数的 "形参不同"(参数个数不同、参数类型不同、参数类型顺序不同)。这一特性让函数调用更灵活,体现了 C++ 的多态思想,而 C 语言不支持函数重载。
使用函数重载需注意:
核心条件 :形参不同是唯一判定标准,返回值不同不能作为重载条件(例如void f()和int f()不能构成重载,调用时无法区分);
歧义问题 :若调用时实参与多个重载函数匹配,会引发编译错误(例如void f1()和void f1(int a=10),调用f1()时无法确定选择哪个函数)
cpp
#include <iostream>
using namespace std;
// 1. 参数类型不同
int Add(int left, int right) {
cout << "int Add(int, int)" << endl;
return left + right;
}
double Add(double left, double right) {
cout << "double Add(double, double)" << endl;
return left + right;
}
// 2. 参数个数不同
void f() {
cout << "f()" << endl;
}
void f(int a) {
cout << "f(int a)" << endl;
}
// 3. 参数类型顺序不同
void f(int a, char b) {
cout << "f(int a, char b)" << endl;
}
void f(char b, int a) {
cout << "f(char b, int a)" << endl;
}
int main() {
Add(10, 20); // 调用int版本Add
Add(10.1, 20.2); // 调用double版本Add
f(); // 调用无参f()
f(10); // 调用f(int a)
f(10, 'a'); // 调用f(int a, char b)
f('a', 10); // 调用f(char b, int a)
return 0;
}
五. 引用:变量的 "别名"
引用并非新定义变量,而是为已存在的变量取 "别名",编译器不会为引用开辟内存空间,引用与原变量共用同一块内存。例如 "李逵" 的别名是 "铁牛",操作 "铁牛" 等同于操作 "李逵"。
引用的核心特性与用法:
定义方式 :类型& 引用名 = 原变量(例如int a=10; int& b=a;,b 是 a 的别名),定义时必须初始化(否则编译报错);
特性 :一个变量可以有多个引用(例如int& c=a;,c 也是 a 的别名),且引用一旦绑定某个变量,就不能再绑定其他变量(例如int c=20; b=c;是将 c 的值赋给 b,而非让 b 成为 c 的别名);
const 引用 :可引用 const 对象或临时对象(如a*3、double转int的中间结果),避免 "权限放大"。例如const int& rb = a*3合法,int& rb = a*3非法(临时对象具有常性,普通引用无法绑定);
应用场景 :主要用于 "引用传参"(替代指针,简化代码,例如void Swap(int& x, int& y)实现交换)和 "引用返回值"(减少拷贝,提高效率,例如int& STTop(ST& rs)返回栈顶元素)。
编译器建议 :inline是编译器的 "建议",而非 "强制"。若函数是递归函数或代码量较大,编译器会忽略inline(展开后会导致代码膨胀),仅对 "频繁调用的短小函数"(如 Add、Swap)生效;
特性:nullptr 是关键字,属于 "指针类型字面量",仅能隐式转换为指针类型(不能转换为 int),避免歧义;
用法 :f(nullptr)会明确调用f(int*),是 C++ 中表示 "空指针" 的标准方式
六. inline 内联函数:提升调用效率
inline 函数 是用inline修饰的函数,编译时编译器会在调用处 "展开" 函数代码,避免函数调用的栈帧开销(普通函数调用需创建栈帧、保存寄存器、恢复现场,有性能损耗)。
使用 inline 函数需注意:
编译器建议 :inline是编译器的 "建议",而非 "强制"。若函数是递归函数或代码量较大,编译器会忽略inline(展开后会导致代码膨胀),仅对 "频繁调用的短小函数"(如 Add、Swap)生效;
声明与定义:不能将 inline 函数的声明与定义分离到不同文件(例如声明在 F.h,定义在 F.cpp),否则链接时会报错(inline 函数展开后无函数地址,链接器无法找到定义);
替代宏函数 :C 语言的宏函数(如#define ADD(a,b) ((a)+(b)))存在 "语法复杂""无法调试" 等问题,inline 函数兼具宏函数的效率和普通函数的安全性,是宏函数的理想替代。
七. nullptr:规范的空指针
C 语言中NULL是宏,在 C++ 中可能被定义为0(而非void*),这会导致 "空指针调用歧义"。例如调用f(int)和f(int*)时,f(NULL)会调用f(int)(因为 NULL 被解析为 0),与 "调用指针版本" 的初衷相悖。
C++11 引入nullptr,解决了这一问题:
特性:nullptr 是关键字,属于 "指针类型字面量",仅能隐式转换为指针类型(不能转换为 int),避免歧义;
用法 :f(nullptr)会明确调用f(int*),是 C++ 中表示 "空指针" 的标准方式
cpp
#include <iostream>
using namespace std;
void f(int x) {
cout << "f(int x)" << endl;
}
void f(int* ptr) {
cout << "f(int* ptr)" << endl;
}
int main() {
f(0); // 调用f(int x)
f(NULL); // 调用f(int x)(NULL被解析为0)
f((int*)NULL); // 调用f(int* ptr)(强制转换为指针)
f(nullptr); // 调用f(int* ptr)(nullptr是指针类型)
return 0;
}