C++ 是在 C 语言的基础上发展起来的一种 支持面向对象编程 的语言。
面向过程是一种以过程(Process)或函数(Function)为中心的编程方法。程序被看作是由一系列顺序执行的步骤(命令、语句)构成的,强调从上到下的控制流程。
以函数为单位组织代码,关注的是“做什么、怎么做”;
数据和函数是分开的;
常见操作:调用函数、传递数据;
比较适合小规模程序或控制流程明确的问题;
常用结构:顺序、选择(if)、循环(for/while);
C++ 兼容 C 语言,同时引入了类、对象、继承、多态、模板等特性。
应用广泛:系统软件、游戏引擎、图形渲染、嵌入式系统等。
以下是 C++入门 的基础知识总结,适合初学者快速掌握 C++ 的基本语法与概念:
一个最简单的 C++ 程序:
#include // 头文件
using namespace std; // 使用标准命名空间
int main() {
cout << "Hello, C++!" << endl;
return 0; // 主函数返回值
}
类型 |
描述 |
示例 |
---|---|---|
int |
整型 |
int a = 10; |
float |
单精度浮点型 |
float f = 3.14; |
double |
双精度浮点型 |
double d = 3.14159; |
char |
字符 |
char c = ‘A’; |
bool |
布尔值 |
bool flag = true; |
const int SIZE = 100; // 常量
#define PI 3.14 // 宏常量
引用就是某个变量的别名,是对一个已存在变量的“另一个名字”。引用必须初始化,不能像指针那样先声明再赋值:
引用一旦绑定,就不能更换绑定对象
引用本身不占用内存(它不像指针存一个地址,它是变量的另一个名字)
int a =0; int& ra = a; //ra是a的引用,也就是别名,a再取了一个名称
int main(){ int a = 10; int& ref = a; // ref 是 a 的引用,即别名 ref = 20; // 实际修改的是 a cout << a; // 输出 20 }
int a = 10; cin >> a; // 从标准输入读取 cout << "a = " << a; // 输出变量
输出为:
a = 10;
+ - * / %
== != > < >= <=
&& || !
=, += ,-=, *=, /=,
if (a > 0) {
cout << "Positive";
} else if (a < 0) {
cout << "Negative";
} else {
cout << "Zero";
}
switch (a) {
case 1:
cout << "One";
break;
default:
cout << "Other";
}
for (int i = 0; i < 10; i++) {
cout << i;
}
while (a < 10) {
a++;
}
do {
a--;
} while (a > 0);
int add(int x, int y) {
return x + y;
}
int main() {
int result = add(3, 4);
}
当函数定义了参数,但是在调用函数时并没有参入实参,则函数会默认使用缺省参数
#include
//using namespace std //c++库中的所用东西都是放在std命名空间中的 // 展开常用库里面的一下对象。 using std::cout; using std::endl; void Print_Hello(int num = 1){ for(int i = 0;i 输出结果如下:
hello world!
如果传入了参数如 num = 5输出结果为:
hello world!
hello world!
hello world!
hello world!
hello world!
函数的参数全部为缺省参数:
//全缺省 void Func1(int a = 10,int b = 20,int c = 30){ cout << "a = " << a << endl; cout << "b = " << b << endl; cout << "c = " << c << endl; } int main(){ Func1(); Func1(1); Func1(1,2); Func1(1,2,3); }
输出结果:
a = 10
b = 20
c = 30
a = 1
b = 20
c = 30
a = 1
b = 2
c = 30
a = 1
b = 2
c = 3
函数的参数部分为缺省参数必须从右往左缺省,而且必须连续缺省:
//半缺省(缺省部分参数) void Func2(int a,int b, int c = 30){ cout << "a = " << a << endl; cout << "b = " << b << endl; cout << "c = " << c << endl; } int main(){ Func2(1,2); Func2(1,2,3); }
输出结果为:
a = 1
b = 2
c = 30
a = 1
b = 2
c = 3
c++ 允许在同一作用域中声明几个功能相似的同名函数,要求函数名相同,参数不同(类型/个数/顺序不同);返回值没有要求。但是如果两个函数只有返回值不同,无法构成函数重载
int Add(int left,int right){ return left+right; } double Add(double left,double right){ return left+right; } long Add(long left,long right){ return left+right; } int Add(int left,int mid,int right){ return left+mid+right; } int main(){ printf("%d\n",Add(10,20)); printf("%d\n",Add(10,20)); printf("%ld\n",Add(10L,20L)); printf("%d\n",Add(10,30,20)); return 0; }
输出结果:
30
30
30
60
1、什么是函数重载?
2、c++如何支持函数重载,为什么c语言不支持呢?
因为 C++ 是“名字改编”(name mangling)支持语言。
也就是说,编译器会根据函数名 + 参数类型,生成唯一的函数符号名(符号重整):
项目 |
C |
C++ |
---|---|---|
文件后缀 |
.c |
.cpp, .cc, .cxx |
编译器调用 |
clang 或 gcc |
clang++ 或 g++ |
是否支持函数重载 |
❌ 不支持 |
✅ 支持 |
生成符号名称 |
函数名保持原样 |
函数名被“修饰”(Mangled) |
是否支持类、模板 |
❌ 不支持 |
✅ 支持 |
比如现在我有这样两个add函数分别用c++和c语言来实现:
c语言的add.c文件代码如下:
int add(int a, int b) { return a + b; } int main(){ return 0; }
然后你对add.c文件进行编译产生add.o的可执行文件
clang add.c -o add.o
然后查看编译后的函数符号(在命令行窗口使用nm进行查看.o):
nm add.o
输出如下
0000000100000000 T __mh_execute_header
0000000100000328 T _add
0000000100000348 T _main你会发现 编译后的文件并没有定义的add函数名做过的的修饰
c++的func.cpp文件 代码如下
int add(int a, int b) { return a + b; } int main(){ return 0; }
而你对fun.cpp文件进行编译产生fun.c的可执行文件
nm fun.o
输出如下:
0000000100000360 T __Z3addii
0000000100000000 T __mh_execute_header
0000000100000380 T _main编译后的文件中,定义的add函数名被修饰了
如果此时你再定一个add的函数重载,在进行编译。
int add(int a, int b) { return a + b; } //函数重载 double add(double a, double b) { return a + b; } int main(){ return 0;
再进行编译后;利用
nm fun.o
输出如下:
0000000100000380 T __Z3adddd
0000000100000360 T __Z3addii
0000000100000000 T __mh_execute_header
00000001000003a0 T _main此时编译后你可以看到两个被修饰的add函数名
通过 名称修饰(Name Mangling) 来区分函数:
函数声明 |
编译后符号名(伪示例) |
---|---|
int add(int, int) |
__Z3addii |
float add(float, float) |
__Z3addff |
而c语言不支持函数名称修饰,多个同名函数会导致链接冲突。
当我需要在c语言项目中调用使用c++的静态或者动态库,如何实现呢?
例如我有一个函数声明如下:
void* tcmalloc(size_t n);
当我们在c++的头文件申明中:
利用 extern “c” 告诉 C++ 编译器:不要对声明的函数做 name mangling(名字修饰),以便生成兼容 C 的符号名称。
// wrapper.hpp 或 tcmalloc.h(供 C 使用的头文件) #ifdef __cplusplus extern "C" { #endif void* tcmalloc(size_t n); // 声明导出的 C 接口 #ifdef __cplusplus } #endif
extern "C" |
告诉 C++ 编译器不要对函数名做 name mangling,生成 C 风格接口 |
int arr[5] = {1, 2, 3, 4, 5};
string str = "Hello";
char name[] = "Tom";
int a = 10;
int* p = &a; // 指针
int& ref = a; // 引用
引用就是某个变量的别名,是对一个已存在变量的“另一个名字”。引用必须初始化,不能像指针那样先声明再赋值:
引用一旦绑定,就不能更换绑定对象
引用本身不占用内存(它不像指针存一个地址,它是变量的另一个名字)
int a =0; int& ra = a; //ra是a的引用,也就是别名,a再取了一个名称
int main(){ int a = 10; int& ref = a; // ref 是 a 的引用,即别名 ref = 20; // 实际修改的是 a cout << a; // 输出 20 }
引用只是一个地址的别名,没有独立空间,和引用的实体共用同一快空间。
项目 |
引用(Reference) |
指针(Pointer) |
---|---|---|
是否可以为 NULL |
❌ 不可以 |
✅ 可以为 nullptr |
是否可重新指向 |
❌ 不可以(固定) |
✅ 可以指向别的变量 |
是否需要解引用 |
❌ 不需要,直接使用 |
✅ 需要用 *ptr 解引用 |
是否必须初始化 |
✅ 必须立即初始化 |
❌ 可以先定义再赋值 |
语法简洁性 |
✅ 更简洁 |
❌ 稍复杂 |
避免值拷贝,提高效率,还能在函数中修改原始变量!
void swap(int& a, int& b) { int temp = a; a = b; b = temp; }
可以让函数直接返回原变量的引用(⚠️但不能返回局部变量引用)
int& getElement(int arr[], int index) { return arr[index]; } int main() { int nums[3] = {1, 2, 3}; getElement(nums, 1) = 10; // 修改数组第二个元素 }
引用可以支持隐式类型转换,但有一些关键限制,尤其在函数参数传递中表现明显。
void foo(double& x) { x = x + 1; } int main() { int a = 5; // foo(a); ❌ 错误:int 无法隐式转换为 double&,因为引用要求“精确匹配类型” }
解决方案
void foo(const double& x) { cout << x << endl; } int main() { int a = 5; foo(a); // ✅ OK:int 可隐式转换为 const double& }
const T& 引用可以绑定到一个临时变量(由隐式转换生成的)。
普通的 T& 引用不可以绑定到临时变量。
理解:因为非 const 引用意味着你可以修改它指向的对象,而临时变量的生命周期很短,可能在语句结束就销毁,所以为了安全性,C++ 禁止非 const 引用绑定临时变量。
int main(){ //权限的放大,不能把const给到非const const int a = 10; // int& b = a; 错误,权限不能放大 int b = 10; const int& c = b; //权限可以缩小 cout << "c = " << c; }
引用类型 |
是否允许绑定临时变量 |
是否支持隐式转换 |
---|---|---|
T& |
❌ 不允许 |
❌ 不支持 |
const T& |
✅ 允许 |
✅ 支持 |
void func(int& x) { cout << "int& version\n"; } void func(const double& x) { cout << "const double& version\n"; } int main() { int a = 10; func(a); // 匹配 int& func(3.14); // 匹配 const double&,从 double 到引用 }
函数返回的是某个变量的别名(引用),而不是它的值(拷贝)。
int a = 10; int& getA() { return a; }
getA() = 20; // 直接修改了变量 a
✅ 这样做可以提高效率,也允许我们修改函数外部的对象。
因为你可能返回了一个已经被销毁的对象引用!
int& foo() { int x = 10; // 局部变量,函数结束后内存释放 return x; // ❌ 返回无效引用 } int main() { int& ref = foo(); // 未定义行为! cout << ref; // 很可能是垃圾值或崩溃 }
原因:
x 是栈上的局部变量,函数执行完就被销毁。
返回它的引用是 悬垂引用(Dangling Reference)。
int& bar() { return 10; // ❌ 返回的是临时变量的引用 }
原因:
字面量 10 会生成一个临时值,只在语句中有效。
尝试返回它的引用是不合法的。
int arr[5] = {1, 2, 3, 4, 5}; int& get(int index) { return arr[index]; // 合法:数组元素在全局或 main 中有效 } get(2) = 10; // 修改 arr[2] 的值
原因:
arr是定义的全局变量,在全局域,main函数中都是有效的
int& foo() { static int x = 10; return x; // 安全:x 的生命周期是整个程序期间 }
原因:
static修饰的变量,变量的生命周期变为整个程序运行期间。不会因为栈的销毁,变量被销毁,但是静态局部变量只在函数第一次调用时初始化一次,以后不会重新初始化。
int& Add2(int a,int b){ static int c = a+b; return c; } int main(){ Add2(3,4); int& ret = Add2(1,2); cout << "Add2(1,2) is :" <
这段代码输出结果是:
Add2(1,2) is :7原因分析:
第一次调用函数,static int c = a + b; 被执行,c = 7
返回 c 的引用(没有使用结果)
函数再次被调用,但注意:static int c = a + b; 不会执行,c 保持为第一次设定的值:7
所以返回的是 c(值为 7)的引用,ret 绑定到这个值
输出就是:Add2(1,2) is :7
如何修正呢?:
int& Add2(int a,int b){ static int c; c = a+b; return c; }
即可,这样c不会被初始化,但是c会一直执行 c = a + b;
引用返回需要变量出了函数的作用域中仍然有效才能使用引用返回。引用可以减少临时变量的出现,提高效率。
场景 |
是否安全 |
原因 |
---|---|---|
返回局部变量的引用 |
❌ 不安全 |
函数结束后变量消失 |
返回临时值的引用 |
❌ 不安全 |
临时变量生命周期太短 |
返回全局变量的引用 |
✅ 安全 |
全局变量生命周期贯穿程序始终 |
返回静态变量的引用 |
✅ 安全 |
静态变量不会在函数结束时销毁 |
返回容器/对象的成员引用(非局部) |
✅ 安全 |
只要容器还存在就是安全的 |
命名空间中可以定义变量,也可以定义函数
namespace myns { int a = 5; int Add(int a,int b){ return a+b; } } #include
using namespace std; //c++库的所用东西都是放到std命名空间中 namespace MySpace { int a = 10; void sayHello() { cout << "Hello from MySpace!" << endl; } } int main() { MySpace::sayHello(); // 使用作用域限定符 :: cout << MySpace::a << endl; return 0; }
这种方式会把命名空间里的所有成员导入到当前作用域。
using namespace MySpace; int main() { sayHello(); // 不用写 MySpace:: }
using MySpace::sayHello; int main() { sayHello(); // ok // cout << a; // 错误,因为没有导入 a }
.cpp 是源代码文件
.h 是头文件,通常包含函数声明、结构体定义等
使用 #include 引入头文件
头文件 |
作用 |
---|---|
|
输入输出 |
|
数学函数 |
|
随机数、内存操作等 |
|
时间处理 |
|
字符串类 |