C++基础:关于C与C++的学习记录
导读:很多初学者在从C语言过渡到C++时,往往只学到了"带类的C",而忽略了C++在底层机制、内存管理和IO流上的全面革新。本文带你系统梳理C与C++的核心差异,详解C++新增语法、流式IO以及堆内存管理(含智能指针),以下是我在职坐标的学习记录:
一、C与C++的区别
C++并非仅仅是C语言的超集,它在设计哲学和底层实现上都有着本质的不同。以下是两者的核心区别对比:
| 对比维度 | C语言 | C++ |
|---|---|---|
| 编程范式 | 面向过程(Procedure-Oriented) | 多范式(面向对象、泛型、函数式等) |
| 类型系统 | 弱类型,隐式转换多 | 强类型,类型检查更严格 |
| 标准库 | 标准C库(stdio.h, string.h等) | STL(容器、算法、迭代器)及IO流 |
| 异常处理 | 依赖返回值和setjmp/longjmp |
try-catch-throw 异常处理机制 |
| 泛型编程 | 宏定义或void*指针 |
模板(Template) |
| 命名空间 | 无,易产生命名冲突 | namespace 解决命名污染 |
| 函数/运算符 | 不支持重载 | 支持函数重载与运算符重载 |
| 内联函数 | 仅支持宏定义 #define |
支持 inline 关键字 |
| 内存管理 | malloc/free |
new/delete 及智能指针 |
1. 编程范式差异
C语言注重步骤和算法,而C++引入了面向对象(OOP),将数据和操作数据的方法封装在一起。
// C语言风格:数据与操作分离
struct Point { int x; int y; };
void move(Point* p, int dx) { p->x += dx; }
// C++风格:封装
class Point {
int x, y;
public:
void move(int dx) { x += dx; }
};
2. 引用与指针的区别
C++引入了引用(Reference),它是已存在变量的别名,必须在定义时初始化,且不能重新绑定,没有空引用的概念。
int a = 10;
int &ref = a; // 引用
int *ptr = &a; // 指针
ref = 20; // 直接修改了a的值
*ptr = 30; // 通过解引用修改a的值
二、C++在C基础语法上新增的内容
C++在C的基础上极大地丰富了语法糖和底层特性,以下是核心新增内容:
1. 类与结构体(class vs struct)
在C++中,struct 和 class 的唯一区别是默认访问权限。struct 默认是 public,而 class 默认是 private。
struct MyStruct {
int data; // 默认 public
};
class MyClass {
int data; // 默认 private
};
2. 函数重载与默认参数
C++允许同名函数拥有不同的参数列表(编译器通过名称修饰 Name Mangling 实现)。同时支持默认参数,但默认参数必须从右向左依次定义。
void print(int a) { /*...*/ }
void print(int a, int b) { /*...*/ } // 重载
void log(const char* msg, int level = 0) { /*...*/ } // 默认参数
3. 构造函数、析构函数与 this 指针
C++的类可以拥有生命周期管理函数。this 指针是一个隐含的指针,指向调用成员函数的对象实例。
class Student {
std::string name;
public:
Student(const std::string& n) : name(n) {} // 构造函数
~Student() {} // 析构函数
void show() const {
std::cout << this->name << std::endl; // this指针
}
};
4. const 的增强
在C++中,const 不仅可以修饰变量,还可以修饰指针、引用,甚至修饰成员函数(保证不修改成员变量)。
const int* p1; // 指向常量的指针
int* const p2; // 常量指针
void func() const {} // const成员函数
5. 强类型枚举(enum class)
C++11 引入了 enum class,避免了传统枚举的隐式类型转换和命名空间污染。
enum class Color { Red, Green, Blue };
Color c = Color::Red;
// int x = c; // 编译错误!不能隐式转换为int
6. 模板
模板实现了编译时的泛型编程;异常处理将错误处理逻辑与正常业务逻辑分离。
// 模板
template<typename T>
T add(T a, T b) { return a + b; }
三、C++的输入和输出
C++ 使用 <iostream> 库进行流式IO,相比C语言的 printf/scanf,它类型安全且易于扩展。
1. cin 和 cout 的基本用法
使用 << 插入运算符和 >> 提取运算符。
#include <iostream>
int main() {
std::string name;
int age;
std::cout << "Enter name and age: ";
std::cin >> name >> age;
std::cout << "Hello " << name << ", you are " << age << std::endl;
return 0;
}
2. 格式化输出
借助 <iomanip> 头文件,可以非常方便地控制输出格式。
#include <iostream>
#include <iomanip>
int main() {
double pi = 3.1415926;
// 固定小数位,保留3位
std::cout << std::fixed << std::setprecision(3) << pi << std::endl;
// 宽度为10,不足部分用'*'填充
std::cout << std::setw(10) << std::setfill('*') << 42 << std::endl;
return 0;
}
3. 输入输出状态标志
流对象有内部状态,可以通过 good(), fail(), eof(), bad() 进行检查。
int num;
std::cin >> num;
if (std::cin.fail()) {
std::cout << "Input is not a number!" << std::endl;
std::cin.clear(); // 清除错误标志
std::cin.ignore(10000, '\n'); // 忽略缓冲区残留字符
}
4. 文件输入输出
使用 <fstream> 进行文件读写。
#include <fstream>
int main() {
// 写入文件
std::ofstream outFile("test.txt");
outFile << "Hello, File IO!" << std::endl;
outFile.close();
// 读取文件
std::ifstream inFile("test.txt");
std::string line;
while (std::getline(inFile, line)) {
std::cout << line << std::endl;
}
return 0;
}
5. 标准I/O对象的关联与取消关联
默认情况下,C++的 cin/cout 与C的 stdin/stdout 是绑定的。在需要极高IO性能的场景(如算法竞赛),可以取消关联:
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr); // 解除cin与cout的绑定
四、C++的堆内存申请和释放
C++将堆内存管理的控制权交给了程序员,但也提供了更安全的机制。
1. new 和 delete 的基本用法
new 不仅分配内存,还会调用构造函数;delete 释放内存前会调用析构函数。
int* p = new int(42); // 分配并初始化
delete p; // 释放内存
p = nullptr; // 防止悬空指针
2. new[] 和 delete[]
对于数组,必须使用 new[] 和 delete[] 匹配,否则会导致未定义行为(尤其是类对象数组)。
int* arr = new int[10];
delete[] arr;
3. new 的初始化方式
int* a = new int; // 默认初始化(值不确定)
int* b = new int(); // 值初始化(初始化为0)
int* c = new int(10); // 直接初始化
int* d = new int{20}; // 列表初始化(C++11)
4. 智能指针
RAII(资源获取即初始化)是C++的核心思想:将资源的生命周期绑定到对象的生命周期。现代C++强烈建议使用智能指针代替裸指针。
#include <memory>
int main() {
// unique_ptr: 独占所有权
auto up = std::make_unique<int>(42);
// shared_ptr: 共享所有权,引用计数
auto sp1 = std::make_shared<int>(100);
auto sp2 = sp1; // 引用计数变为2
// weak_ptr: 不增加引用计数,解决循环引用
std::weak_ptr<int> wp = sp1;
return 0; // 离开作用域,内存自动释放
}
5. new/delete 与 malloc/free 的对比
| 特性 | malloc / free | new / delete |
|---|---|---|
| 类型 | C库函数 | C++运算符 |
| 构造/析构 | 不调用 | 自动调用 |
| 返回值 | void*,需强转 |
具体类型指针 |
| 失败处理 | 返回 NULL |
抛出异常 / nothrow |
| 内存大小 | 需手动计算字节数 | 自动计算 |
最佳实践:在现代C++中,永远不要手动 new/delete。优先使用容器(如
std::vector)和智能指针(std::unique_ptr,std::shared_ptr)。
总结与展望
从C到C++,我们跨越的不仅仅是语法的增加,更是编程思维的升华。C++在保留C语言高效底层控制力的同时,通过面向对象、泛型编程和RAII等机制,极大地提升了代码的抽象能力和安全性。
浙公网安备 33010602011771号