C++ 语法入门指南(面向前端开发者)
从 JavaScript 到 C++:帮助前端开发者快速理解 C++ 语法和 Chromium 项目中的特殊用法
目录
1. 基础概念对比
1.1 JavaScript vs C++ 核心差异
| 特性 | JavaScript | C++ |
|---|---|---|
| 类型系统 | 动态类型 | 静态类型(编译时检查) |
| 内存管理 | 自动垃圾回收 | 手动管理(智能指针辅助) |
| 编译 | 解释执行(JIT) | 编译为机器码 |
| 文件组织 | 模块(import/export) | 头文件 + 实现文件 |
| null/undefined | null, undefined | nullptr, 不存在 undefined |
| 函数重载 | ❌ 不支持 | ✅ 支持 |
| 类继承 | 原型链/class | 显式继承 |
1.2 代码对比:Hello World
JavaScript:
// 定义
function sayHello(name) {
console.log(`Hello, ${name}!`);
}
// 调用
sayHello("World");
C++:
// 声明(通常在头文件中)
void SayHello(const std::string& name);
// 定义(通常在 .cc 文件中)
void SayHello(const std::string& name) {
LOG(INFO) << "Hello, " << name << "!";
}
// 调用
SayHello("World");
2. 文件组织与导入
2.1 头文件 (.h) vs 实现文件 (.cc/.cpp)
这是 C++ 最重要的概念之一,与 JS 的模块系统完全不同。
JavaScript 模块系统
// utils.js - 导出
export function add(a, b) {
return a + b;
}
export class Calculator {
multiply(a, b) {
return a * b;
}
}
// main.js - 导入
import { add, Calculator } from './utils.js';
console.log(add(1, 2));
const calc = new Calculator();
C++ 头文件系统
头文件 (utils.h) - 声明接口
// 防止重复包含(头文件保护)
#ifndef UTILS_H_
#define UTILS_H_
#include <string> // 导入标准库
// 声明函数(告诉编译器这个函数存在)
int Add(int a, int b);
// 声明类(定义类的结构)
class Calculator {
public:
Calculator(); // 构造函数
~Calculator(); // 析构函数
int Multiply(int a, int b); // 方法声明
private:
int result_; // 私有成员变量
};
#endif // UTILS_H_
实现文件 (utils.cc) - 实现细节
#include "utils.h" // 导入自己的头文件
#include <iostream>
// 实现函数
int Add(int a, int b) {
return a + b;
}
// 实现类的构造函数
Calculator::Calculator() : result_(0) {
std::cout << "Calculator created\n";
}
// 实现类的析构函数
Calculator::~Calculator() {
std::cout << "Calculator destroyed\n";
}
// 实现类的方法
int Calculator::Multiply(int a, int b) {
result_ = a * b;
return result_;
}
使用文件 (main.cc)
#include "utils.h" // 导入我们的头文件
int main() {
// 使用函数
int sum = Add(1, 2);
// 使用类
Calculator calc;
int product = calc.Multiply(3, 4);
return 0;
}
2.2 #include 指令(相当于 import)
// 系统/标准库头文件 - 使用尖括号
#include <string> // C++ 标准库字符串
#include <vector> // 动态数组
#include <memory> // 智能指针
// 项目内部头文件 - 使用引号
#include "my_class.h"
#include "utils/helper.h"
// Chromium 的头文件
#include "base/logging.h"
#include "chrome/browser/ui/browser.h"
#include "ui/views/view.h"
重要概念:
- ✅
#include相当于 复制粘贴 整个头文件内容到当前位置 - ✅ 头文件只 声明,实现文件 定义
- ✅ 头文件保护(
#ifndef)防止重复包含 - ✅ 现代 C++ 也可以用
#pragma once替代
2.3 前向声明(减少依赖)
// bad.h - 完整包含(增加编译时间)
#include "chrome/browser/ui/browser.h"
class MyClass {
public:
void DoSomething(Browser* browser);
private:
Browser* browser_;
};
// good.h - 前向声明(只声明存在,不需要完整定义)
class Browser; // 前向声明:告诉编译器 Browser 是个类
class MyClass {
public:
void DoSomething(Browser* browser);
private:
Browser* browser_; // 只需要指针,不需要知道 Browser 的细节
};
// good.cc - 在实现文件中包含完整头文件
#include "my_class.h"
#include "chrome/browser/ui/browser.h" // 在这里才需要完整定义
void MyClass::DoSomething(Browser* browser) {
browser->DoSomething(); // 这里才需要知道 Browser 的方法
}
3. 变量与数据类型
3.1 基本类型对比
| JavaScript | C++ | 说明 |
|---|---|---|
let x = 5 | int x = 5; | 整数 |
let x = 3.14 | double x = 3.14; | 浮点数 |
let x = "hello" | std::string x = "hello"; | 字符串 |
let x = true | bool x = true; | 布尔值 |
let x = null | int* x = nullptr; | 空指针 |
let x | ❌ 必须指定类型 | 未定义 |
3.2 C++ 类型详解
// 整数类型(有符号)
int x = 42; // 通常 32 位
short y = 100; // 通常 16 位
long z = 1000000; // 通常 32 或 64 位
long long w = 999999999; // 至少 64 位
// 整数类型(无符号 - 只能是正数)
unsigned int count = 10;
size_t length = 100; // 用于大小和索引
// 浮点数
float pi = 3.14f; // 单精度(32 位)
double e = 2.71828; // 双精度(64 位,推荐)
// 字符和字符串
char c = 'A'; // 单个字符
std::string str = "Hello"; // 字符串
std::u16string u16str = u"你好"; // UTF-16 字符串(Chromium 常用)
// 布尔值
bool is_valid = true;
bool is_empty = false;
// 常量
const int MAX_SIZE = 100; // 运行时常量
constexpr int SIZE = 10; // 编译时常量(更优化)
// 类型推导(C++11 起)
auto a = 5; // 推导为 int
auto b = 3.14; // 推导为 double
auto c = "hello"; // 推导为 const char*
auto d = std::string("hello"); // 推导为 std::string
3.3 变量声明方式
// JavaScript
let x = 5;
const y = 10;
var z = 15;
// C++
int x = 5; // 普通变量
const int y = 10; // 常量(不可修改)
static int z = 15; // 静态变量(生命周期持续整个程序)
// Chromium 风格
constexpr int kMaxSize = 100; // 编译时常量(k 前缀)
const int kDefaultValue = 42; // 运行时常量
3.4 数组和容器
JavaScript:
let arr = [1, 2, 3, 4, 5];
arr.push(6);
let first = arr[0];
let length = arr.length;
C++:
// 方式1: 固定大小数组(类似 JS 的 Array)
int arr[5] = {1, 2, 3, 4, 5};
int first = arr[0];
int length = 5; // 需要手动记住大小
// 方式2: std::vector(动态数组,推荐)
#include <vector>
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.push_back(6); // 添加元素
int first = vec[0]; // 访问元素
size_t length = vec.size(); // 获取大小
// 遍历
for (int i = 0; i < vec.size(); i++) {
LOG(INFO) << vec[i];
}
// 或者使用范围 for(C++11,类似 JS 的 for...of)
for (int value : vec) {
LOG(INFO) << value;
}
// 或者使用迭代器
for (auto it = vec.begin(); it != vec.end(); ++it) {
LOG(INFO) << *it;
}
3.5 对象和字典
JavaScript:
let obj = {
name: "Alice",
age: 30,
city: "Beijing"
};
console.log(obj.name);
console.log(obj["age"]);
C++:
// 方式1: std::map(有序字典)
#include <map>
std::map<std::string, std::string> obj;
obj["name"] = "Alice";
obj["city"] = "Beijing";
std::string name = obj["name"];
// 遍历
for (const auto& [key, value] : obj) {
LOG(INFO) << key << ": " << value;
}
// 方式2: std::unordered_map(无序字典,性能更好)
#include <unordered_map>
std::unordered_map<std::string, int> ages;
ages["Alice"] = 30;
ages["Bob"] = 25;
// 方式3: 结构体(类似 TypeScript 的 interface)
struct Person {
std::string name;
int age;
std::string city;
};
Person alice = {"Alice", 30, "Beijing"};
LOG(INFO) << alice.name; // 访问成员
4. 函数与方法
4.1 函数定义
JavaScript:
// 普通函数
function add(a, b) {
return a + b;
}
// 箭头函数
const multiply = (a, b) => a * b;
// 默认参数
function greet(name = "Guest") {
return `Hello, ${name}!`;
}
C++:
// 普通函数
int Add(int a, int b) {
return a + b;
}
// 默认参数(只能在声明时指定)
std::string Greet(const std::string& name = "Guest") {
return "Hello, " + name + "!";
}
// 函数重载(同名函数,不同参数)
int Add(int a, int b) {
return a + b;
}
double Add(double a, double b) {
return a + b;
}
std::string Add(const std::string& a, const std::string& b) {
return a + b;
}
// 调用
int sum1 = Add(1, 2); // 调用第一个
double sum2 = Add(1.5, 2.5); // 调用第二个
std::string sum3 = Add("Hello", " World"); // 调用第三个
4.2 参数传递方式
// 1. 按值传递(复制一份)- 适合小对象
void ProcessInt(int value) {
value = 100; // 不影响外部
}
int x = 5;
ProcessInt(x);
LOG(INFO) << x; // 仍然是 5
// 2. 按引用传递(直接修改原对象)- 高效,可修改
void ProcessIntRef(int& value) {
value = 100; // 修改外部变量
}
int y = 5;
ProcessIntRef(y);
LOG(INFO) << y; // 变成 100
// 3. 按常量引用传递(只读,不复制)- 推荐用于大对象
void ProcessString(const std::string& str) {
LOG(INFO) << str;
// str = "new"; // ❌ 编译错误:不能修改
}
std::string msg = "Hello";
ProcessString(msg); // 高效:不复制字符串
// 4. 按指针传递(C 风格)
void ProcessIntPtr(int* ptr) {
if (ptr != nullptr) {
*ptr = 100;
}
}
int z = 5;
ProcessIntPtr(&z); // 传递地址
推荐规则(Chromium 风格):
- ✅ 小对象(int, bool, 指针):按值传递
- ✅ 大对象(string, vector):按
const引用传递 - ✅ 需要修改:按引用传递
- ✅ 可选参数:按指针传递(可以传
nullptr)
4.3 Lambda 表达式(类似箭头函数)
JavaScript:
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(x => x * 2);
setTimeout(() => {
console.log("Hello");
}, 1000);
C++:
#include <vector>
#include <algorithm>
std::vector<int> numbers = {1, 2, 3, 4, 5};
// Lambda 表达式
auto doubled = std::vector<int>();
std::transform(numbers.begin(), numbers.end(),
std::back_inserter(doubled),
[](int x) { return x * 2; });
// 捕获外部变量
int factor = 3;
auto tripled = [factor](int x) { return x * factor; };
int result = tripled(5); // 15
// Chromium 中的常见用法:回调
base::OnceCallback<void()> callback = base::BindOnce([]() {
LOG(INFO) << "Callback executed";
});
// 延迟执行
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce([]() {
LOG(INFO) << "Delayed task";
}),
base::Seconds(1));
Lambda 语法详解:
// [捕获列表](参数列表) -> 返回类型 { 函数体 }
// 1. 不捕获,不接受参数
auto f1 = []() { LOG(INFO) << "Hello"; };
// 2. 按值捕获 x
int x = 5;
auto f2 = [x]() { LOG(INFO) << x; };
// 3. 按引用捕获 x
auto f3 = [&x]() { x = 10; };
// 4. 捕获所有变量(按值)
auto f4 = [=]() { LOG(INFO) << x; };
// 5. 捕获所有变量(按引用)
auto f5 = [&]() { x = 20; };
// 6. 混合捕获
auto f6 = [x, &y]() { LOG(INFO) << x << y; };
// 7. 捕获 this(在类的方法中)
auto f7 = [this]() { this->DoSomething(); };
5. 类与面向对象
5.1 类定义
JavaScript (ES6):
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`Hello, I'm ${this.name}`);
}
static create(name) {
return new Person(name, 0);
}
}
const alice = new Person("Alice", 30);
alice.greet();
C++:
// person.h
class Person {
public:
// 构造函数
Person(const std::string& name, int age);
// 析构函数(JS 没有这个概念)
~Person();
// 公开方法
void Greet();
// Getter/Setter
std::string GetName() const { return name_; }
void SetName(const std::string& name) { name_ = name; }
// 静态方法
static Person* Create(const std::string& name);
private:
// 私有成员变量
std::string name_;
int age_;
};
// person.cc
Person::Person(const std::string& name, int age)
: name_(name), // 初始化列表(推荐)
age_(age) {
LOG(INFO) << "Person created: " << name_;
}
Person::~Person() {
LOG(INFO) << "Person destroyed: " << name_;
}
void Person::Greet() {
LOG(INFO) << "Hello, I'm " << name_;
}
Person* Person::Create(const std::string& name) {
return new Person(name, 0);
}
// 使用
Person alice("Alice", 30); // 栈上创建
alice.Greet();
Person* bob = new Person("Bob", 25); // 堆上创建
bob->Greet();
delete bob; // 手动释放内存
5.2 继承
JavaScript:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
console.log(`${this.name} barks`);
}
}
const dog = new Dog("Buddy", "Golden");
dog.speak();
C++:
// animal.h
class Animal {
public:
explicit Animal(const std::string& name);
virtual ~Animal(); // 虚析构函数(多态必需)
// 虚函数:可以被子类覆盖
virtual void Speak();
// 纯虚函数:子类必须实现(类似抽象方法)
virtual void Move() = 0;
protected: // 子类可以访问
std::string name_;
};
// dog.h
class Dog : public Animal { // public 继承
public:
Dog(const std::string& name, const std::string& breed);
~Dog() override; // override 关键字:明确覆盖
// 覆盖父类方法
void Speak() override;
void Move() override;
private:
std::string breed_;
};
// dog.cc
Dog::Dog(const std::string& name, const std::string& breed)
: Animal(name), // 调用父类构造函数
breed_(breed) {
}
void Dog::Speak() {
LOG(INFO) << name_ << " barks";
}
void Dog::Move() {
LOG(INFO) << name_ << " runs";
}
// 使用
Dog dog("Buddy", "Golden");
dog.Speak(); // "Buddy barks"
// 多态
Animal* animal = new Dog("Max", "Husky");
animal->Speak(); // "Max barks" (调用 Dog 的 Speak)
delete animal;
5.3 访问控制
class MyClass {
public:
// 公开:任何地方都可以访问
void PublicMethod();
protected:
// 保护:只有子类可以访问
void ProtectedMethod();
private:
// 私有:只有当前类可以访问
void PrivateMethod();
int private_data_;
};
5.4 Chromium 中的类定义规范
// my_view.h
#ifndef CHROME_BROWSER_UI_VIEWS_MY_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_MY_VIEW_H_
#include "ui/views/view.h"
#include "ui/base/metadata/metadata_header_macros.h"
class Browser;
namespace views {
class MyView : public View {
METADATA_HEADER(MyView, View) // Chromium 元数据宏
public:
// 构造函数
explicit MyView(Browser* browser);
// 禁止拷贝和赋值(Chromium 规范)
MyView(const MyView&) = delete;
MyView& operator=(const MyView&) = delete;
// 析构函数
~MyView() override;
// View overrides(覆盖父类方法)
void OnPaint(gfx::Canvas* canvas) override;
gfx::Size CalculatePreferredSize(
const SizeBounds& available_size) const override;
private:
// 私有方法
void UpdateUI();
// 成员变量使用 _ 后缀
raw_ptr<Browser> browser_;
std::string title_;
bool is_active_ = false;
};
} // namespace views
#endif // CHROME_BROWSER_UI_VIEWS_MY_VIEW_H_
// my_view.cc
#include "chrome/browser/ui/views/my_view.h"
#include "base/logging.h"
#include "chrome/browser/ui/browser.h"
namespace views {
MyView::MyView(Browser* browser)
: browser_(browser),
title_("Default Title") {
LOG(INFO) << "MyView created";
}
MyView::~MyView() {
LOG(INFO) << "MyView destroyed";
}
void MyView::OnPaint(gfx::Canvas* canvas) {
View::OnPaint(canvas); // 调用父类方法
// 自定义绘制
}
void MyView::UpdateUI() {
// 私有方法实现
}
BEGIN_METADATA(MyView) // 元数据实现
END_METADATA
} // namespace views
6. 指针与引用
这是 C++ 最重要也最容易困惑的概念。JavaScript 没有这些概念。
6.1 什么是指针?
指针 = 内存地址
int x = 42; // 普通变量
int* ptr = &x; // 指针:存储 x 的地址
int value = *ptr; // 解引用:获取指针指向的值
LOG(INFO) << "x 的值: " << x; // 42
LOG(INFO) << "x 的地址: " << &x; // 0x7fff5fbff5bc (示例)
LOG(INFO) << "ptr 的值: " << ptr; // 0x7fff5fbff5bc
LOG(INFO) << "ptr 指向的值: " << *ptr; // 42
图示:
内存:
+--------+--------+
| x: 42 | ptr: →-|--→ 指向 x
+--------+--------+
地址: 0x100 0x104
6.2 指针基础操作
// 声明指针
int* ptr1; // 未初始化(危险!)
int* ptr2 = nullptr; // 空指针(推荐初始化方式)
// 获取地址(& 运算符)
int x = 10;
int* ptr = &x; // ptr 指向 x
// 解引用(* 运算符)
*ptr = 20; // 修改 x 的值
LOG(INFO) << x; // 20
// 指针运算
int arr[5] = {1, 2, 3, 4, 5};
int* p = arr; // 指向数组第一个元素
LOG(INFO) << *p; // 1
LOG(INFO) << *(p + 1); // 2
LOG(INFO) << *(p + 2); // 3
// 空指针检查(非常重要!)
if (ptr != nullptr) {
*ptr = 100;
} else {
LOG(ERROR) << "Null pointer!";
}
6.3 什么是引用?
引用 = 别名
int x = 10;
int& ref = x; // ref 是 x 的别名
ref = 20; // 修改 ref 就是修改 x
LOG(INFO) << x; // 20
// 引用必须初始化
int& ref2; // ❌ 编译错误
// 引用不能重新绑定
int y = 30;
ref = y; // 这不是重新绑定,而是把 y 的值赋给 x
int x = 4; int& a = x;
a == x;
int* y = &x;
*y == x;
6.4 指针 vs 引用
| 特性 | 指针 | 引用 |
|---|---|---|
| 可以为空 | ✅ nullptr | ❌ 必须有效 |
| 可以重新指向 | ✅ 可以 | ❌ 绑定后不变 |
| 语法 | *ptr 解引用 | 直接使用 |
| 用途 | 动态内存、可选参数 | 函数参数 |
// 指针示例
void ModifyByPointer(int* ptr) {
if (ptr != nullptr) {
*ptr = 100;
}
}
int x = 5;
ModifyByPointer(&x); // 传递地址
ModifyByPointer(nullptr); // 可以传空
// 引用示例
void ModifyByReference(int& ref) {
ref = 100; // 直接修改
}
int y = 5;
ModifyByReference(y); // 直接传递变量
// ModifyByReference(nullptr); // ❌ 不能传空
6.5 const 指针和引用
// 1. 指向常量的指针(不能通过指针修改值)
const int* ptr1 = &x;
int const* ptr2 = &x; // 同上
// *ptr1 = 10; // ❌ 不能修改
ptr1 = &y; // ✅ 可以指向其他变量
// 2. 常量指针(不能改变指向)
int* const ptr3 = &x;
*ptr3 = 10; // ✅ 可以修改值
// ptr3 = &y; // ❌ 不能改变指向
// 3. 指向常量的常量指针
const int* const ptr4 = &x;
// *ptr4 = 10; // ❌ 不能修改值
// ptr4 = &y; // ❌ 不能改变指向
// 4. 常量引用(最常用于函数参数)
const int& ref = x;
// ref = 20; // ❌ 不能修改
推荐规则:
- ✅ 函数参数:使用
const Type&(只读) - ✅ 可选参数:使用
Type*(可以传nullptr) - ✅ 需要修改:使用
Type&或Type*
7. 内存管理
JavaScript 有自动垃圾回收,C++ 需要手动管理内存。
7.1 栈 vs 堆
// 栈内存(自动管理,作用域结束自动释放)
void StackExample() {
int x = 5; // 栈上分配
std::string str = "hi"; // 栈上分配
Person person("Alice", 30); // 栈上分配
// 函数结束,所有变量自动释放
}
// 堆内存(手动管理,需要 delete)
void HeapExample() {
int* ptr = new int(5); // 堆上分配
std::string* str = new std::string("hi");
Person* person = new Person("Alice", 30);
// 使用...
// 必须手动释放!
delete ptr;
delete str;
delete person;
}
// 数组
int* arr = new int[10]; // 分配数组
delete[] arr; // 必须用 delete[]
7.2 智能指针(推荐,自动管理内存)
Chromium 和现代 C++ 使用智能指针避免内存泄漏。
std::unique_ptr - 独占所有权
#include <memory>
// 创建
std::unique_ptr<Person> person = std::make_unique<Person>("Alice", 30);
// 使用(像普通指针一样)
person->Greet();
LOG(INFO) << person->GetName();
// 获取原始指针
Person* raw_ptr = person.get();
// 转移所有权(移动)
std::unique_ptr<Person> person2 = std::move(person);
// 现在 person 为空,person2 拥有对象
// 函数结束,person2 自动释放内存
Chromium 中的用法:
class MyView : public views::View {
public:
MyView() {
// 创建子视图(unique_ptr)
auto label = std::make_unique<views::Label>(u"Hello");
// 添加到父视图(转移所有权)
label_ = AddChildView(std::move(label));
// label 已经无效,使用 label_ 访问
}
private:
raw_ptr<views::Label> label_; // 不拥有,只是观察
};
std::shared_ptr - 共享所有权
// 多个指针共享同一个对象
std::shared_ptr<Person> person1 = std::make_shared<Person>("Bob", 25);
std::shared_ptr<Person> person2 = person1; // 共享所有权
// 引用计数
LOG(INFO) << person1.use_count(); // 2
person2.reset(); // 释放 person2
LOG(INFO) << person1.use_count(); // 1
// 最后一个 shared_ptr 释放时,对象被删除
Chromium 的 base::WeakPtr - 弱引用
class MyClass : public base::SupportsWeakPtr<MyClass> {
public:
void DoSomething() {
base::WeakPtr<MyClass> weak_this = AsWeakPtr();
// 延迟执行任务
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&MyClass::OnTimeout, weak_this),
base::Seconds(1));
}
void OnTimeout() {
LOG(INFO) << "Timeout";
}
};
// 即使对象被删除,weak_ptr 也不会崩溃
7.3 Chromium 的智能指针
// raw_ptr<T> - 原始指针的安全封装
class MyView : public views::View {
private:
raw_ptr<Browser> browser_; // ✅ 推荐
// Browser* browser_; // ❌ 避免
};
// base::WeakPtr<T> - 弱引用
base::WeakPtr<MyClass> weak_ptr = obj->AsWeakPtr();
if (weak_ptr) { // 检查对象是否还存在
weak_ptr->DoSomething();
}
// scoped_refptr<T> - 引用计数智能指针
scoped_refptr<base::RefCountedData<int>> data =
base::MakeRefCounted<base::RefCountedData<int>>(42);
8. 模板与泛型
类似于 TypeScript 的泛型。
8.1 函数模板
TypeScript:
function identity<T>(arg: T): T {
return arg;
}
let result1 = identity<number>(5);
let result2 = identity<string>("hello");
C++:
template <typename T>
T Identity(T arg) {
return arg;
}
// 使用
int result1 = Identity<int>(5);
std::string result2 = Identity<std::string>("hello");
// 或者自动推导
auto result3 = Identity(5); // 推导为 int
auto result4 = Identity("hello"); // 推导为 const char*
8.2 类模板
// 定义模板类
template <typename T>
class Box {
public:
explicit Box(T value) : value_(value) {}
T GetValue() const { return value_; }
void SetValue(T value) { value_ = value; }
private:
T value_;
};
// 使用
Box<int> int_box(42);
Box<std::string> str_box("hello");
LOG(INFO) << int_box.GetValue();
LOG(INFO) << str_box.GetValue();
8.3 STL 容器(都是模板)
#include <vector>
#include <map>
#include <set>
// vector<T> - 动态数组
std::vector<int> numbers = {1, 2, 3};
std::vector<std::string> names = {"Alice", "Bob"};
// map<K, V> - 键值对
std::map<std::string, int> ages;
ages["Alice"] = 30;
ages["Bob"] = 25;
// set<T> - 集合(去重)
std::set<int> unique_numbers = {1, 2, 2, 3, 3};
// unique_numbers 只包含 {1, 2, 3}
9. 命名空间
类似于 JavaScript 的模块,用于避免命名冲突。
9.1 定义和使用
// 定义命名空间
namespace myproject {
namespace utils {
void PrintMessage(const std::string& msg) {
LOG(INFO) << msg;
}
class Helper {
public:
void DoSomething();
};
} // namespace utils
} // namespace myproject
// 使用命名空间
myproject::utils::PrintMessage("Hello");
myproject::utils::Helper helper;
helper.DoSomething();
// 使用 using 简化
using myproject::utils::PrintMessage;
PrintMessage("Hello"); // 不需要前缀
// 使用 namespace 别名
namespace mu = myproject::utils;
mu::PrintMessage("Hello");
9.2 Chromium 的命名空间
// views 命名空间
namespace views {
class View { };
class Button : public View { };
}
// 使用
views::View* view = new views::View();
views::Button* button = new views::Button();
// 或者在 .cc 文件中
namespace views {
// 在这里可以直接使用 View, Button 等
void MyFunction() {
View* view = new View(); // 不需要 views:: 前缀
}
} // namespace views
9.3 匿名命名空间(文件内部可见)
// my_file.cc
namespace {
// 这些只在当前文件可见(类似 JS 的模块作用域)
constexpr int kMaxSize = 100;
void HelperFunction() {
// 实现
}
} // namespace
// 可以在文件内使用
void PublicFunction() {
HelperFunction(); // OK
LOG(INFO) << kMaxSize;
}
10. Chromium 特有语法
10.1 宏定义
// 条件编译
#if defined(OS_WIN)
// Windows 特有代码
#elif defined(OS_MAC)
// macOS 特有代码
#elif defined(OS_LINUX)
// Linux 特有代码
#endif
// 功能开关
#if BUILDFLAG(IS_CHROMEOS_ASH)
// ChromeOS 代码
#endif
// DCHECK - Debug 模式断言
DCHECK(pointer != nullptr);
DCHECK_EQ(a, b); // a == b
DCHECK_NE(a, b); // a != b
DCHECK_LT(a, b); // a < b
// LOG - 日志
LOG(INFO) << "Information";
LOG(WARNING) << "Warning";
LOG(ERROR) << "Error";
DLOG(INFO) << "Debug only";
10.2 base:: 库常用工具
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/time/time.h"
#include "base/strings/string_util.h"
// 回调绑定
void OnClick() {
LOG(INFO) << "Clicked";
}
base::RepeatingCallback<void()> callback = base::BindRepeating(&OnClick);
callback.Run(); // 执行回调
// 时间
base::TimeDelta delay = base::Seconds(5);
base::TimeDelta ms = base::Milliseconds(500);
base::Time now = base::Time::Now();
// 字符串工具
std::string upper = base::ToUpperASCII("hello"); // "HELLO"
bool starts = base::StartsWith(str, "prefix");
std::vector<std::string> parts = base::SplitString(
"a,b,c", ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
10.3 METADATA 宏
// 头文件
class MyView : public views::View {
METADATA_HEADER(MyView, View)
// ...
};
// 实现文件
BEGIN_METADATA(MyView)
END_METADATA
10.4 DISALLOW_COPY_AND_ASSIGN(禁止拷贝)
// 旧写法
class MyClass {
public:
MyClass();
private:
DISALLOW_COPY_AND_ASSIGN(MyClass);
};
// 现代写法(推荐)
class MyClass {
public:
MyClass();
MyClass(const MyClass&) = delete;
MyClass& operator=(const MyClass&) = delete;
};
10.5 base::BindOnce vs base::BindRepeating
// BindOnce - 只能调用一次
base::OnceCallback<void()> once = base::BindOnce([]() {
LOG(INFO) << "Called once";
});
std::move(once).Run(); // 必须用 std::move
// once.Run(); // ❌ 已经失效
// BindRepeating - 可以多次调用
base::RepeatingCallback<void()> repeat = base::BindRepeating([]() {
LOG(INFO) << "Called multiple times";
});
repeat.Run(); // 第一次
repeat.Run(); // 第二次
// 带参数
auto callback = base::BindOnce([](int x, const std::string& s) {
LOG(INFO) << x << " " << s;
}, 42, "hello");
std::move(callback).Run();
// 绑定类方法
class MyClass {
public:
void OnEvent(int value) {
LOG(INFO) << "Value: " << value;
}
};
MyClass obj;
auto callback = base::BindRepeating(&MyClass::OnEvent,
base::Unretained(&obj));
callback.Run(100);
11. 常用标准库
11.1 字符串
#include <string>
// 创建
std::string str1 = "Hello";
std::string str2("World");
std::string str3(5, 'A'); // "AAAAA"
// 拼接
std::string full = str1 + " " + str2; // "Hello World"
str1 += "!"; // "Hello!"
// 长度
size_t len = str1.length();
size_t size = str1.size(); // 同上
// 访问
char first = str1[0];
char last = str1[str1.length() - 1];
// 子字符串
std::string sub = str1.substr(0, 5); // 从位置0开始,长度5
// 查找
size_t pos = str1.find("ll"); // 返回位置,找不到返回 std::string::npos
if (pos != std::string::npos) {
LOG(INFO) << "Found at " << pos;
}
// 替换
str1.replace(0, 5, "Hi"); // 替换前5个字符
// 转换
int num = std::stoi("123"); // string to int
double d = std::stod("3.14"); // string to double
std::string s = std::to_string(42); // int to string
// Chromium 中使用 UTF-16
std::u16string u16str = u"你好";
std::string utf8 = base::UTF16ToUTF8(u16str);
11.2 vector(动态数组)
#include <vector>
// 创建
std::vector<int> vec1;
std::vector<int> vec2 = {1, 2, 3, 4, 5};
std::vector<int> vec3(10); // 10个元素,默认值0
std::vector<int> vec4(10, 42); // 10个元素,值都是42
// 添加元素
vec1.push_back(10);
vec1.push_back(20);
vec1.emplace_back(30); // 更高效
// 访问元素
int first = vec2[0];
int second = vec2.at(1); // 带边界检查
int last = vec2.back();
// 大小
size_t size = vec2.size();
bool empty = vec2.empty();
// 删除元素
vec2.pop_back(); // 删除最后一个
vec2.erase(vec2.begin()); // 删除第一个
vec2.clear(); // 清空
// 遍历
for (size_t i = 0; i < vec2.size(); i++) {
LOG(INFO) << vec2[i];
}
for (int value : vec2) {
LOG(INFO) << value;
}
// 排序
#include <algorithm>
std::sort(vec2.begin(), vec2.end());
// 查找
auto it = std::find(vec2.begin(), vec2.end(), 10);
if (it != vec2.end()) {
LOG(INFO) << "Found at index " << (it - vec2.begin());
}
11.3 map(字典)
#include <map>
// 创建
std::map<std::string, int> ages;
// 插入
ages["Alice"] = 30;
ages["Bob"] = 25;
ages.insert({"Charlie", 35});
// 访问
int age = ages["Alice"];
// 检查存在
if (ages.find("Alice") != ages.end()) {
LOG(INFO) << "Alice exists";
}
// 删除
ages.erase("Bob");
// 遍历
for (const auto& [name, age] : ages) { // C++17
LOG(INFO) << name << ": " << age;
}
// 或者
for (const auto& pair : ages) {
LOG(INFO) << pair.first << ": " << pair.second;
}
// unordered_map - 更快但无序
#include <unordered_map>
std::unordered_map<std::string, int> fast_ages;
11.4 set(集合)
#include <set>
std::set<int> numbers = {1, 2, 3, 4, 5};
// 插入
numbers.insert(6);
numbers.insert(3); // 不会重复插入
// 检查存在
if (numbers.count(3) > 0) {
LOG(INFO) << "3 exists";
}
// 删除
numbers.erase(3);
// 遍历(自动排序)
for (int num : numbers) {
LOG(INFO) << num;
}
11.5 optional(可选值)
#include <optional>
// 类似 TypeScript 的 T | undefined
std::optional<int> maybe_value;
// 设置值
maybe_value = 42;
// 检查是否有值
if (maybe_value.has_value()) {
LOG(INFO) << "Value: " << maybe_value.value();
}
// 或者简写
if (maybe_value) {
LOG(INFO) << "Value: " << *maybe_value;
}
// 获取值或默认值
int value = maybe_value.value_or(0);
// 清空
maybe_value.reset();
12. 如何查找定义
这是前端开发者最困惑的地方!
12.1 在 VS Code / Cursor 中查找
方法1: 跳转到定义(最常用)
// 假设你看到这样的代码
views::Button* button = new views::Button();
button->SetEnabled(true);
操作:
-
将光标放在
Button上 -
按
F12或 右键 → "Go to Definition" -
跳转到
ui/views/controls/button/button.h -
将光标放在
SetEnabled上 -
按
F12 -
跳转到方法定义
方法2: 查找所有引用
void MyMethod();
- 光标放在
MyMethod上 - 按
Shift + F12或 右键 → "Find All References" - 看到所有调用这个方法的地方
方法3: 查找符号
- 按
Ctrl + T(Windows) 或Cmd + T(Mac) - 输入符号名称,如
Button - 看到所有匹配的类、函数
方法4: 全局搜索
- 按
Ctrl + Shift + F - 输入
class Button - 找到类定义
12.2 理解 C++ 的查找规则
// 1. 包含头文件
#include "ui/views/controls/button/button.h"
// 2. 使用命名空间
namespace views {
// 3. 在这个命名空间内可以直接使用 Button
void CreateButton() {
Button* btn = new Button(); // 不需要 views::
}
} // namespace views
// 4. 在命名空间外需要加前缀
void CreateButtonOutside() {
views::Button* btn = new views::Button(); // 需要 views::
}
12.3 查找继承关系
class MyButton : public views::Button {
// ...
};
想知道 Button 有哪些方法?
- 跳转到
views::Button定义(F12) - 查看头文件中的 public 方法
- 注意
override的方法(来自父类)
// ui/views/controls/button/button.h
class Button : public View {
public:
void SetEnabled(bool enabled); // Button 自己的方法
// View overrides
void OnPaint(gfx::Canvas* canvas) override; // 继承自 View
};
- 如果想知道
OnPaint的原始定义 - 跳转到
View类(按住 Ctrl 点击View) - 在
ui/views/view.h中找到OnPaint的声明
12.4 常见查找场景
场景1: 不知道方法在哪里定义
button->SetBackground(views::CreateSolidBackground(color));
问题: CreateSolidBackground 在哪里?
方法:
- 光标放在
CreateSolidBackground上 - 按 F12 → 跳转到
ui/views/background.h - 看到函数声明:
std::unique_ptr<Background> CreateSolidBackground(SkColor color);
场景2: 不知道怎么调用父类方法
class MyView : public views::View {
void OnPaint(gfx::Canvas* canvas) override {
// 调用父类方法
View::OnPaint(canvas); // 使用 类名::
// 自定义绘制
}
};
场景3: 不知道头文件路径
错误信息:
error: unknown type name 'Browser'
解决:
- 全局搜索
class Browser(Ctrl + Shift + F) - 找到
chrome/browser/ui/browser.h - 添加 include:
#include "chrome/browser/ui/browser.h"
场景4: 看到宏不知道是什么
METADATA_HEADER(MyView, View)
- 全局搜索
#define METADATA_HEADER - 找到
ui/base/metadata/metadata_header_macros.h - 看到宏定义:
#define METADATA_HEADER(class_name, parent_class_name) \
public: \
using ParentClass = parent_class_name; \
// ...
12.5 使用 Chrome Code Search(在线查看)
如果本地找不到,可以在线查看:
- 访问 source.chromium.org/chromium
- 搜索类名或方法名
- 查看完整的代码和引用
示例:
- 搜索
views::Button - 看到所有定义和使用的地方
- 可以在线浏览代码
13. 实战示例:对比 JS 和 C++
13.1 创建按钮并处理点击
JavaScript (React):
import React from 'react';
function MyComponent() {
const handleClick = () => {
console.log('Button clicked');
window.open('https://www.google.com');
};
return (
<button
onClick={handleClick}
style={{ background: 'blue', color: 'white' }}
>
Click Me
</button>
);
}
C++ (Chromium):
// my_button.h
#ifndef MY_BUTTON_H_
#define MY_BUTTON_H_
#include "ui/views/controls/button/button.h"
class Browser;
class MyButton : public views::Button {
public:
explicit MyButton(Browser* browser);
~MyButton() override;
private:
void OnButtonPressed();
raw_ptr<Browser> browser_;
};
#endif // MY_BUTTON_H_
// my_button.cc
#include "my_button.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "ui/views/background.h"
MyButton::MyButton(Browser* browser)
: Button(base::BindRepeating(&MyButton::OnButtonPressed,
base::Unretained(this))),
browser_(browser) {
// 设置样式
SetBackground(views::CreateSolidBackground(
SkColorSetRGB(0, 0, 255))); // 蓝色背景
// 设置文本
SetText(u"Click Me");
}
MyButton::~MyButton() = default;
void MyButton::OnButtonPressed() {
LOG(INFO) << "Button clicked";
// 打开网页
NavigateParams params(browser_,
GURL("https://www.google.com"),
ui::PAGE_TRANSITION_LINK);
params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
Navigate(¶ms);
}
13.2 管理状态
JavaScript:
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+1</button>
</div>
);
}
C++:
class CounterView : public views::View {
public:
CounterView() : count_(0) {
auto* layout = SetLayoutManager(
std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical));
// 创建标签
label_ = AddChildView(std::make_unique<views::Label>(
u"Count: 0"));
// 创建按钮
auto* button = AddChildView(
std::make_unique<views::Button>(
base::BindRepeating(&CounterView::Increment,
base::Unretained(this))));
button->SetText(u"+1");
}
private:
void Increment() {
count_++;
label_->SetText(base::UTF8ToUTF16(
"Count: " + std::to_string(count_)));
}
int count_;
raw_ptr<views::Label> label_;
};
14. 学习路线建议
阶段1: 基础语法(1-2周)
- ✅ 变量、类型、函数
- ✅ 类和继承
- ✅ 指针和引用(重点)
- ✅ 头文件和实现文件
阶段2: 标准库(1周)
- ✅ string, vector, map
- ✅ 智能指针
- ✅ 算法库(sort, find 等)
阶段3: Chromium 特有(1-2周)
- ✅ base:: 库(回调、时间、字符串)
- ✅ Views 框架
- ✅ 宏和元数据
- ✅ 内存管理规范
阶段4: 实践(持续)
- ✅ 阅读项目代码
- ✅ 修改现有组件
- ✅ 创建新组件
15. 常见问题 FAQ
Q1: 为什么有的变量后面有 _?
A: Chromium 代码规范,成员变量使用 _ 后缀。
class MyClass {
private:
int count_; // 成员变量
std::string name_; // 成员变量
raw_ptr<Browser> browser_; // 成员变量
};
Q2: const 应该放在哪里?
A: 多种用法:
const int x = 5; // 常量
const std::string& str; // 常量引用(推荐用于参数)
int* const ptr; // 常量指针
const int* ptr; // 指向常量的指针
void GetValue() const; // 常量方法(不修改成员变量)
Q3: nullptr vs NULL vs 0?
A: 使用 nullptr(C++11 推荐)
int* ptr = nullptr; // ✅ 推荐
int* ptr = NULL; // ⚠️ 旧写法
int* ptr = 0; // ❌ 避免
Q4: 什么时候用 . 什么时候用 ->?
A:
.用于对象->用于指针
MyClass obj;
obj.Method(); // 对象用 .
MyClass* ptr = &obj;
ptr->Method(); // 指针用 ->
// 等价于
(*ptr).Method();
Q5: std::move 是什么?
A: 转移所有权,避免复制。
std::unique_ptr<Widget> widget1 = std::make_unique<Widget>();
std::unique_ptr<Widget> widget2 = std::move(widget1);
// 现在 widget1 为空,widget2 拥有对象
Q6: 头文件为什么要有 #ifndef?
A: 防止重复包含。
// bad.h
class MyClass { };
// 如果被包含两次,会导致重复定义错误
// good.h
#ifndef GOOD_H_
#define GOOD_H_
class MyClass { };
#endif // GOOD_H_
// 或者现代写法
#pragma once
class MyClass { };
16. 速查表
类型对照
| JavaScript | C++ |
|---|---|
number | int, double |
string | std::string |
boolean | bool |
Array<T> | std::vector<T> |
Map<K,V> | std::map<K,V> |
Set<T> | std::set<T> |
null/undefined | nullptr |
T | undefined | std::optional<T> |
常用操作
| 操作 | JavaScript | C++ |
|---|---|---|
| 输出 | console.log() | LOG(INFO) << |
| 字符串拼接 | "a" + "b" | "a" + std::string("b") |
| 数组长度 | arr.length | arr.size() |
| 添加元素 | arr.push(x) | arr.push_back(x) |
| 遍历 | for (x of arr) | for (auto x : arr) |
| Lambda | x => x * 2 | [](int x) { return x * 2; } |
希望这份指南能帮助你快速从 JavaScript 过渡到 C++ Chromium 开发!🚀