你可以把 C++ 看作是 C 语言的一个“升级版”,它包含了 C 语言的所有特性,并增加了许多新功能,使其更适合大型、复杂的软件开发。

(图片来源网络,侵删)
下面我们从几个核心维度来详细对比它们的区别。
核心区别一览表
| 特性 | C语言 | C++语言 |
|---|---|---|
| 编程范式 | 过程式/面向过程 (Procedural) | 多范式 (Multi-paradigm): 过程式、面向对象、泛型编程 |
| 核心思想 | 函数是核心,通过函数调用来组织代码和数据。 | 类和对象是核心,强调数据和操作数据的函数(方法)的封装。 |
| 关键字 | 约32个 (如 int, void, for, if, struct) |
约63个 (包含了C的所有关键字,并增加了 class, new, delete, this, template, virtual 等) |
| 内存管理 | 手动管理,使用 malloc() 和 free() 函数。 |
手动 + 自动管理,除了 malloc()/free(),主要使用 new/delete 运算符,并有RAII机制和智能指针。 |
| 输入/输出 | 使用 C标准库 的函数,如 printf(), scanf(), fopen(), fclose()。 |
使用 iostream库 的流对象,如 cin, cout, cerr。 |
| 数据结构 | 使用 struct 结构体,只包含数据,不包含函数。 |
使用 class 类,可以同时包含数据和函数(方法)。struct 也可以包含函数,但默认成员为 public。 |
| 函数重载 | 不支持,每个函数名必须是唯一的。 | 支持,允许定义多个同名函数,只要它们的参数列表(类型、数量、顺序)不同即可。 |
| 引用 | 没有引用,所有参数传递都是值传递(指针是传递地址的值)。 | 支持引用,引用是变量的别名,可以用于函数参数,实现“引用传递”,避免拷贝和指针的复杂性。 |
| 异常处理 | 没有内置的异常处理机制,通常通过返回错误码或 setjmp/longjmp 来处理错误。 |
有内置的异常处理机制,使用 try, catch, throw 关键字,可以更优雅地处理运行时错误。 |
| 泛型编程 | 不支持,需要使用 void* 指针或宏来实现类似功能,类型安全性差。 |
支持,通过 模板 实现强大的泛型编程,可以编写与类型无关的代码(如 std::vector<T>)。 |
| 命名空间 | 没有命名空间,所有全局标识符都共享同一个命名空间,容易产生命名冲突。 | 支持命名空间,使用 namespace 关键字来组织代码,避免命名冲突(如 std::cout)。 |
| 面向对象特性 | 不支持,没有类、继承、多态、封装等概念。 | 完全支持,这是C++最重要的特性之一。 |
| 标准库 | 相对较小,主要关注底层操作和基本数据类型。 | 非常庞大和强大,包含了 STL (Standard Template Library),如容器 (vector, list, map)、算法、迭代器等。 |
核心差异详解
编程思想:过程式 vs. 面向对象
这是两者最根本的区别。
-
C语言 (过程式):
- 程序的执行流程是线性的,围绕函数展开。
- 数据和操作数据的函数是分离的,你定义一个
struct Student来存储学生信息,然后写一个printStudent()函数来打印它。 - 当程序规模变大时,数据与函数分离的缺点会暴露出来:数据可能被全局的任意函数随意修改,导致程序难以维护和理解。
-
C++ (面向对象):
- 程序围绕对象展开。
- 封装:将数据(属性)和操作这些数据的函数(方法)捆绑在一起,形成一个类,对象是类的实例。
class Student内部既包含了数据(如姓名、年龄),也包含了打印自己的方法。 - 继承:允许创建新的类(子类)来继承已有类(父类)的属性和方法,实现了代码的重用和层次化。
- 多态:允许使用父类类型的指针或引用来调用子类对象的同名方法,程序会在运行时决定调用哪个具体的方法,提供了极大的灵活性。
内存管理:malloc/free vs. new/delete 与 RAII
-
C语言:
- 使用
malloc()函数在堆上分配内存,free()函数释放内存。 - 问题:程序员必须记住在不再需要内存时调用
free(),否则会导致内存泄漏。free()一个无效的指针,会导致未定义行为,这是一个非常容易出错的地方。
- 使用
-
C++:
- 引入了
new和delete运算符。new不仅分配内存,还会调用对象的构造函数;delete不仅释放内存,还会调用对象的析构函数。 - RAII (Resource Acquisition Is Initialization):这是C++的核心设计哲学之一,它意味着“资源的获取(如内存分配)应该与对象的初始化绑定,资源的释放(如内存释放)应该与对象的销毁绑定”。
- 智能指针:C++11 引入了
std::unique_ptr,std::shared_ptr等智能指针,它们是RAII的完美体现,当智能指针离开其作用域时,会自动调用delete来释放内存,极大地减少了内存泄漏的风险。
- 引入了
标准库:C库 vs. STL
-
C语言:标准库相对基础,提供了一些底层操作(如文件I/O、字符串处理、数学计算等)。
-
C++:拥有极其强大的 STL,STL分为三大部分:
- 容器:用于存储数据的模板类,如
std::vector(动态数组),std::list(双向链表),std::map(键值对),std::string(字符串)。 - 算法:用于操作容器的函数模板,如
std::sort(),std::find(),std::copy()。 - 迭代器:用于遍历和访问容器元素的“通用指针”。
使用STL,你可以用非常简洁高效的代码完成复杂的任务,而无需自己从头实现数据结构和算法。
- 容器:用于存储数据的模板类,如
代码示例对比
假设我们要创建一个简单的学生结构并打印信息。
C语言版本
#include <stdio.h>
#include <string.h>
// 1. 定义结构体(只包含数据)
struct Student {
char name[50];
int age;
};
// 2. 定义一个函数来打印学生信息(数据与函数分离)
void printStudent(struct Student s) {
printf("Name: %s, Age: %d\n", s.name, s.age);
}
int main() {
// 3. 在栈上创建一个学生对象
struct Student s1;
strcpy(s1.name, "Zhang San");
s1.age = 20;
// 4. 调用函数
printStudent(s1);
return 0;
}
C++版本
#include <iostream>
#include <string>
// 1. 定义类(包含数据和方法)
class Student {
private: // 封装:数据设为私有,外部不能直接访问
std::string name;
int age;
public: // 提供公共接口
// 构造函数
Student(const std::string& n, int a) : name(n), age(a) {}
// 2. 成员函数(方法),直接操作对象内部的数据
void printInfo() const {
std::cout << "Name: " << name << ", Age: " << age << std::endl;
}
};
int main() {
// 3. 创建学生对象
Student s1("Li Si", 21);
// 4. 调用对象的方法
s1.printInfo();
// 5. 使用STL容器
#include <vector>
std::vector<Student> students;
students.push_back(s1); // 添加学生到vector中
return 0;
}
总结与如何选择
| 选择 C 语言 .. | 选择 C++ .. |
|---|---|
| ✅ 开发嵌入式系统、操作系统、驱动程序等对性能和内存控制要求极高的底层软件。 | ✅ 开发大型桌面应用、游戏引擎、图形界面、金融软件等复杂的系统。 |
| ✅ 编写需要极致性能和最小开销的代码。 | ✅ 需要利用面向对象的特性来管理复杂的代码结构和业务逻辑。 |
| ✅ 项目规模较小,逻辑相对简单,过程式编程足够。 | ✅ 需要快速开发,并希望使用丰富的标准库(如STL)来提高效率。 |
| ✅ 你所在的环境或团队主要使用C语言,或者需要与C代码库交互。 | ✅ 需要现代编程语言的特性,如异常处理、泛型编程、自动内存管理等。 |
C++ 是 C 语言的强大演进,它保留了 C 的底层能力,同时通过面向对象、泛型编程等现代特性,让开发者能够更高效、更安全地构建大型、复杂的软件系统,对于现代软件开发,尤其是应用层开发,C++ 通常是比纯 C 更好的选择。
