C++ 语法入门指南(面向前端开发者)

52 阅读18分钟

C++ 语法入门指南(面向前端开发者)

从 JavaScript 到 C++:帮助前端开发者快速理解 C++ 语法和 Chromium 项目中的特殊用法


目录

  1. 基础概念对比
  2. 文件组织与导入
  3. 变量与数据类型
  4. 函数与方法
  5. 类与面向对象
  6. 指针与引用
  7. 内存管理
  8. 模板与泛型
  9. 命名空间
  10. Chromium 特有语法
  11. 常用标准库
  12. 如何查找定义

1. 基础概念对比

1.1 JavaScript vs C++ 核心差异

特性JavaScriptC++
类型系统动态类型静态类型(编译时检查)
内存管理自动垃圾回收手动管理(智能指针辅助)
编译解释执行(JIT)编译为机器码
文件组织模块(import/export)头文件 + 实现文件
null/undefinednull, undefinednullptr, 不存在 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 基本类型对比

JavaScriptC++说明
let x = 5int x = 5;整数
let x = 3.14double x = 3.14;浮点数
let x = "hello"std::string x = "hello";字符串
let x = truebool x = true;布尔值
let x = nullint* 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);

操作:

  1. 将光标放在 Button

  2. F12 或 右键 → "Go to Definition"

  3. 跳转到 ui/views/controls/button/button.h

  4. 将光标放在 SetEnabled

  5. F12

  6. 跳转到方法定义

方法2: 查找所有引用
void MyMethod();
  1. 光标放在 MyMethod
  2. Shift + F12 或 右键 → "Find All References"
  3. 看到所有调用这个方法的地方
方法3: 查找符号
  1. Ctrl + T (Windows) 或 Cmd + T (Mac)
  2. 输入符号名称,如 Button
  3. 看到所有匹配的类、函数
方法4: 全局搜索
  1. Ctrl + Shift + F
  2. 输入 class Button
  3. 找到类定义

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 有哪些方法?

  1. 跳转到 views::Button 定义(F12)
  2. 查看头文件中的 public 方法
  3. 注意 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
};
  1. 如果想知道 OnPaint 的原始定义
  2. 跳转到 View 类(按住 Ctrl 点击 View
  3. ui/views/view.h 中找到 OnPaint 的声明

12.4 常见查找场景

场景1: 不知道方法在哪里定义
button->SetBackground(views::CreateSolidBackground(color));

问题: CreateSolidBackground 在哪里?

方法:

  1. 光标放在 CreateSolidBackground
  2. 按 F12 → 跳转到 ui/views/background.h
  3. 看到函数声明:
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'

解决:

  1. 全局搜索 class Browser (Ctrl + Shift + F)
  2. 找到 chrome/browser/ui/browser.h
  3. 添加 include:
#include "chrome/browser/ui/browser.h"
场景4: 看到宏不知道是什么
METADATA_HEADER(MyView, View)
  1. 全局搜索 #define METADATA_HEADER
  2. 找到 ui/base/metadata/metadata_header_macros.h
  3. 看到宏定义:
#define METADATA_HEADER(class_name, parent_class_name) \
  public: \
   using ParentClass = parent_class_name; \
   // ...

12.5 使用 Chrome Code Search(在线查看)

如果本地找不到,可以在线查看:

  1. 访问 source.chromium.org/chromium
  2. 搜索类名或方法名
  3. 查看完整的代码和引用

示例:

  • 搜索 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(&params);
}

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周)

  1. ✅ 变量、类型、函数
  2. ✅ 类和继承
  3. ✅ 指针和引用(重点)
  4. ✅ 头文件和实现文件

阶段2: 标准库(1周)

  1. ✅ string, vector, map
  2. ✅ 智能指针
  3. ✅ 算法库(sort, find 等)

阶段3: Chromium 特有(1-2周)

  1. ✅ base:: 库(回调、时间、字符串)
  2. ✅ Views 框架
  3. ✅ 宏和元数据
  4. ✅ 内存管理规范

阶段4: 实践(持续)

  1. ✅ 阅读项目代码
  2. ✅ 修改现有组件
  3. ✅ 创建新组件

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. 速查表

类型对照

JavaScriptC++
numberint, double
stringstd::string
booleanbool
Array<T>std::vector<T>
Map<K,V>std::map<K,V>
Set<T>std::set<T>
null/undefinednullptr
T | undefinedstd::optional<T>

常用操作

操作JavaScriptC++
输出console.log()LOG(INFO) <<
字符串拼接"a" + "b""a" + std::string("b")
数组长度arr.lengtharr.size()
添加元素arr.push(x)arr.push_back(x)
遍历for (x of arr)for (auto x : arr)
Lambdax => x * 2[](int x) { return x * 2; }

希望这份指南能帮助你快速从 JavaScript 过渡到 C++ Chromium 开发!🚀