C++和Node.js快速入门指南(自用)
·
适合有Java/Python基础的前端工程师,30分钟快速了解核心概念,应对面试提问
📘 Part 1: C++ 快速入门
🎯 核心定位
C++是什么?
- 高性能的编译型语言,C语言的扩展
- 支持面向对象、泛型编程、底层操作
- 用于系统开发、游戏引擎、性能敏感场
与Java/Python对比:
Java/Python: 自动内存管理,跨平台虚拟机,开发效率高
C++: 手动内存管理,直接编译为机器码,性能最高,但容易出错
1️⃣ 基础语法 (与Java对比)
程序入口
// C++ 版本
#include <iostream>
using namespace std;
int main() {
cout << "Hello World" << endl;
return 0;
}
// Java 版本
public class Main {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
关键区别:
- C++没有类包裹,main函数直接写
#include相当于import,<iostream>是输入输出库cout相当于System.out,<<是输出运算符endl相当于\n换行
变量声明
// C++: 类型在前,名字在后
int age = 25;
string name = "张三"; // 需要 #include <string>
double price = 99.9;
bool isActive = true;
// 自动推断类型 (C++11)
auto count = 10; // 自动推断为int
auto message = "Hello"; // 推断为const char*
// Java对比
int age = 25;
String name = "张三";
double price = 99.9;
boolean isActive = true;
var count = 10; // Java 10+
2️⃣ 指针和引用
指针 (Pointer)
int num = 10;
int* ptr = # // ptr存储num的内存地址
cout << num; // 输出: 10 (值)
cout << # // 输出: 0x7fff... (地址)
cout << ptr; // 输出: 0x7fff... (地址)
cout << *ptr; // 输出: 10 (解引用,获取地址指向的值)
*ptr = 20; // 通过指针修改值
cout << num; // 输出: 20 (原变量被改变)
关键概念:
&= 取地址运算符 (获取变量的内存地址)*= 解引用运算符 (访问指针指向的值)int*= 指针类型 (指向int的指针)
类比理解:
变量 = 房子
地址 = 门牌号
指针 = 记录门牌号的便签
&num → 查看房子的门牌号
ptr → 便签上写的门牌号
*ptr → 根据便签找到房子,看里面的内容
引用 (Reference)
int num = 10;
int& ref = num; // ref是num的别名
ref = 20; // 修改ref就是修改num
cout << num; // 输出: 20
// Java没有真正的引用,但对象传递类似
指针 vs 引用:
指针:
- 可以为空 (nullptr)
- 可以改变指向
- 需要解引用 (*)
引用:
- 不能为空
- 不能改变引用对象
- 直接使用,不需要解引用
3️⃣ 内存管理
栈 vs 堆
// 栈内存: 自动管理,作用域结束自动释放
void func() {
int x = 10; // 栈上分配
} // x自动销毁
// 堆内存: 手动管理,需要手动释放
void func() {
int* ptr = new int(10); // 堆上分配
// ... 使用ptr
delete ptr; // 必须手动释放!
ptr = nullptr; // 防止野指针
}
// 数组的堆分配
int* arr = new int[100];
delete[] arr; // 注意要用delete[]
内存泄漏示例:
void badFunction() {
int* data = new int(100);
// 忘记delete!
} // data指针被销毁,但堆内存没释放 → 内存泄漏!
类比Java:
// Java: 垃圾回收器自动管理
void func() {
Integer x = new Integer(10);
// 不用管释放,GC会处理
}
智能指针
#include <memory>
// unique_ptr: 独占所有权
unique_ptr<int> ptr1(new int(10));
// unique_ptr<int> ptr2 = ptr1; // 错误!不能复制
// shared_ptr: 共享所有权,引用计数
shared_ptr<int> ptr1 = make_shared<int>(10);
shared_ptr<int> ptr2 = ptr1; // OK,引用计数+1
// 两个都销毁后,内存才释放
// weak_ptr: 弱引用,不增加引用计数
4️⃣ 类和对象
// 定义类
class Student {
private:
string name;
int age;
public:
// 构造函数
Student(string n, int a) : name(n), age(a) {}
// 成员函数
void display() {
cout << "Name: " << name << ", Age: " << age << endl;
}
// getter/setter
string getName() { return name; }
void setName(string n) { name = n; }
};
// 使用
Student s1("张三", 20); // 栈上创建
s1.display();
Student* s2 = new Student("李四", 22); // 堆上创建
s2->display(); // 指针用->访问成员
delete s2;
与Java对比:
C++:
- 可以栈上创建对象 (自动管理)
- 可以堆上创建对象 (需手动管理)
- 指针访问成员用 ->
- 对象访问成员用 .
Java:
- 所有对象都在堆上
- 都是引用访问
- 统一用 . 访问成员
5️⃣ 面试常问概念
Q: 什么是指针?为什么需要指针?
A: 指针是存储内存地址的变量。需要指针因为:
1. 动态内存分配
2. 高效传递大对象(传地址比复制快)
3. 数据结构(链表、树等)
4. 与底层系统交互
Q: 什么是内存泄漏?
A: 程序申请了堆内存但忘记释放,导致内存无法回收。
例如: new了对象但没有delete,随着程序运行内存占用越来越高。
Q: C++和C的区别?
A: C++是C的超集,主要增加了:
1. 面向对象(类、继承、多态)
2. 模板(泛型编程)
3. 异常处理
4. 命名空间
5. STL标准库
Q: C++的优缺点?
优点:
- 性能极高,接近硬件
- 灵活,底层控制力强
- 适合系统开发、游戏引擎
缺点:
- 学习曲线陡峭
- 容易出内存错误
- 开发效率相对低
- 跨平台需要重新编译
🟢 Part 2: Node.js 快速入门
🎯 核心定位
Node.js是什么?
- JavaScript的后端运行环境
- 基于Chrome V8引擎
- 事件驱动、非阻塞I/O
- 适合I/O密集型应用(API服务、实时应用)
与Python对比:
Node.js: 异步非阻塞,单线程事件循环,适合高并发I/O
Python: 同步阻塞为主,多线程/多进程,适合数据处理/AI
1️⃣ 基础概念
运行JS文件
# 创建文件 app.js
node app.js
# 交互式环境(REPL)
node
> console.log("Hello")
模块系统 (CommonJS)
// math.js - 导出模块
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
module.exports = { add, subtract };
// 或: exports.add = add;
// app.js - 导入模块
const math = require('./math');
console.log(math.add(5, 3)); // 8
// 导入内置模块
const fs = require('fs');
const http = require('http');
与前端ES6模块对比:
// CommonJS (Node.js)
const module = require('./module');
module.exports = { ... };
// ES6 (前端)
import module from './module';
export default { ... };
// 注意: Node.js也支持ES6模块,需要配置
2️⃣ 异步编程
回调函数 (Callback)
const fs = require('fs');
// 异步读取文件
fs.readFile('data.txt', 'utf8', (err, data) => {
if (err) {
console.error('读取失败:', err);
return;
}
console.log('文件内容:', data);
});
console.log('这行会先执行!');
执行顺序:
1. 发起读取文件请求
2. 立即执行下一行 (不等待文件读取完成)
3. 输出"这行会先执行!"
4. 文件读取完成后,调用回调函数
5. 输出文件内容
回调地狱 (Callback Hell)
// 糟糕的写法: 嵌套太深
fs.readFile('file1.txt', (err, data1) => {
if (err) return console.error(err);
fs.readFile('file2.txt', (err, data2) => {
if (err) return console.error(err);
fs.readFile('file3.txt', (err, data3) => {
if (err) return console.error(err);
console.log(data1, data2, data3);
});
});
});
Promise (解决方案1)
const fs = require('fs').promises;
// 链式调用
fs.readFile('file1.txt', 'utf8')
.then(data1 => {
console.log(data1);
return fs.readFile('file2.txt', 'utf8');
})
.then(data2 => {
console.log(data2);
})
.catch(err => {
console.error('出错了:', err);
});
async/await (解决方案2)
const fs = require('fs').promises;
async function readFiles() {
try {
const data1 = await fs.readFile('file1.txt', 'utf8');
const data2 = await fs.readFile('file2.txt', 'utf8');
console.log(data1, data2);
} catch (err) {
console.error('出错了:', err);
}
}
readFiles();
3️⃣ 常用内置模块
fs (文件系统)
const fs = require('fs');
// 同步读取 (阻塞)
const data = fs.readFileSync('file.txt', 'utf8');
// 异步读取 (非阻塞,推荐)
fs.readFile('file.txt', 'utf8', (err, data) => {
if (err) throw err;
console.log(data);
});
// 写入文件
fs.writeFile('output.txt', 'Hello', err => {
if (err) throw err;
console.log('写入成功');
});
// 检查文件是否存在
if (fs.existsSync('file.txt')) {
console.log('文件存在');
}
http (创建服务器)
const http = require('http');
const server = http.createServer((req, res) => {
// req: 请求对象
// res: 响应对象
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
});
server.listen(3000, () => {
console.log('服务器运行在 http://localhost:3000');
});
path (路径处理)
const path = require('path');
const filePath = '/users/zhang/documents/file.txt';
path.basename(filePath); // 'file.txt'
path.dirname(filePath); // '/users/zhang/documents'
path.extname(filePath); // '.txt'
// 拼接路径 (跨平台)
const fullPath = path.join(__dirname, 'data', 'file.txt');
// Windows: C:\project\data\file.txt
// Linux: /project/data/file.txt
4️⃣ Express框架 (快速搭建API)
安装和基础使用
npm install express
const express = require('express');
const app = express();
// 中间件: 解析JSON请求体
app.use(express.json());
// GET请求
app.get('/', (req, res) => {
res.send('Hello World');
});
// GET请求带参数
app.get('/user/:id', (req, res) => {
const userId = req.params.id;
res.json({ id: userId, name: '张三' });
});
// POST请求
app.post('/api/login', (req, res) => {
const { username, password } = req.body;
// 验证逻辑...
res.json({ success: true, token: 'xxx' });
});
// 启动服务器
app.listen(3000, () => {
console.log('服务器运行在 3000 端口');
});
实际项目结构
project/
├── app.js # 入口文件
├── routes/ # 路由
│ ├── user.js
│ └── product.js
├── controllers/ # 控制器(业务逻辑)
│ └── userController.js
├── models/ # 数据模型
│ └── User.js
├── middleware/ # 中间件
│ └── auth.js
└── package.json
5️⃣ NPM包管理
常用命令
# 初始化项目
npm init -y
# 安装依赖
npm install express
npm i express # 简写
# 安装开发依赖
npm install --save-dev nodemon
# 全局安装
npm install -g pm2
# 安装指定版本
npm install express@4.17.1
# 卸载
npm uninstall express
# 查看已安装的包
npm list
package.json
{
"name": "my-app",
"version": "1.0.0",
"description": "我的Node.js应用",
"main": "app.js",
"scripts": {
"start": "node app.js",
"dev": "nodemon app.js",
"test": "jest"
},
"dependencies": {
"express": "^4.18.0"
},
"devDependencies": {
"nodemon": "^2.0.20"
}
}
版本号说明:
^4.18.0 → 允许小版本更新 (4.x.x)
~4.18.0 → 允许补丁版本更新 (4.18.x)
4.18.0 → 锁定版本
* → 最新版本
6️⃣ 事件循环 (Event Loop)
核心概念
Node.js是单线程的,但通过事件循环实现异步非阻塞
执行顺序:
1. 同步代码
2. 微任务 (Promise.then, process.nextTick)
3. 宏任务 (setTimeout, setInterval, I/O)
示例
console.log('1: 同步代码');
setTimeout(() => {
console.log('2: setTimeout (宏任务)');
}, 0);
Promise.resolve().then(() => {
console.log('3: Promise (微任务)');
});
console.log('4: 同步代码');
// 输出顺序: 1 → 4 → 3 → 2
为什么这样?
1. 执行同步代码: 输出 1, 4
2. 检查微任务队列: 执行Promise,输出 3
3. 检查宏任务队列: 执行setTimeout,输出 2
7️⃣ 实战案例: RESTful API
const express = require('express');
const app = express();
app.use(express.json());
// 模拟数据库
let users = [
{ id: 1, name: '张三', age: 20 },
{ id: 2, name: '李四', age: 22 }
];
// 获取所有用户
app.get('/api/users', (req, res) => {
res.json(users);
});
// 获取单个用户
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) return res.status(404).json({ error: '用户不存在' });
res.json(user);
});
// 创建用户
app.post('/api/users', (req, res) => {
const newUser = {
id: users.length + 1,
name: req.body.name,
age: req.body.age
};
users.push(newUser);
res.status(201).json(newUser);
});
// 更新用户
app.put('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) return res.status(404).json({ error: '用户不存在' });
user.name = req.body.name;
user.age = req.body.age;
res.json(user);
});
// 删除用户
app.delete('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ error: '用户不存在' });
users.splice(index, 1);
res.status(204).send();
});
app.listen(3000, () => {
console.log('API服务器运行在 3000 端口');
});
8️⃣ 常见问题
Q: Node.js的优缺点?
优点:
- 前后端统一用JavaScript
- 异步非阻塞,适合高并发I/O
- npm生态丰富
- 开发效率高
缺点:
- 单线程,CPU密集型任务性能差
- 回调地狱(已被async/await解决)
- 错误处理不当会导致进程崩溃
Q: Node.js是单线程还是多线程?
A: 主线程是单线程,但底层有线程池处理I/O操作。
用户代码在单线程运行,通过事件循环处理异步任务。
I/O操作(文件读写、网络请求)由libuv线程池处理。
适合: I/O密集型任务
不适合: CPU密集型任务(如复杂计算、图像处理)
Q: 什么是中间件(Middleware)?
A: 在Express中,中间件是处理请求的函数,按顺序执行。
例如:
- 日志记录中间件: 记录每个请求
- 身份验证中间件: 检查用户是否登录
- 错误处理中间件: 统一处理错误
app.use((req, res, next) => {
console.log('请求:', req.url);
next(); // 传递给下一个中间件
});
Q: Promise和async/await的区别?
A: async/await是Promise的语法糖,让异步代码看起来像同步代码。
Promise: .then()链式调用
async/await: 用try/catch处理错误,代码更简洁
本质上async函数返回的也是Promise。
📊 总结对比
| 特性 | C++ | Node.js |
|---|---|---|
| 类型 | 编译型语言 | 运行时环境(JavaScript) |
| 性能 | 极高 | 中等(V8优化后较快) |
| 内存管理 | 手动(new/delete) | 自动(垃圾回收) |
| 并发模型 | 多线程 | 单线程+事件循环 |
| 适用场景 | 系统开发、游戏引擎 | API服务、实时应用 |
| 学习曲线 | 陡峭 | 平缓(对前端友好) |
| 与岗位关系 | Electron原生模块 | 桌面应用后端逻辑 |
更多推荐

所有评论(0)