typedef
typedef T value_type;语法的结构是typedef 原类型 新别名;定义后,value_type 和 T 就是同一个类型,可以互换使用
模板
C++ 模板的声明和定义分离有特殊性,和普通函数/类不一样
- 普通函数可以分离
cpp
// 头文件 add.h
int add(int a, int b); // 声明
// 源文件 add.cpp
int add(int a, int b) { // 定义
return a + b;
}
// main.cpp 可以正常使用
#include "add.h"
int main() {
add(1, 2); // 链接时找到定义
}
- 模板不能这样分离
cpp
// 头文件 add.h
template <typename T>
T add(T a, T b); // 声明
// 源文件 add.cpp
template <typename T>
T add(T a, T b) { // 定义
return a + b;
}
// main.cpp - 链接错误!
#include "add.h"
int main() {
add(1, 2); // 错误:找不到定义
}
原因:编译器看到 add(1, 2) 时需要生成 add(int, int) 的代码,但定义在 .cpp 文件中看不到
解决方案一:全部放在头文件(最常用)
cpp
// add.h - 声明和定义都在头文件
template <typename T>
T add(T a, T b); // 声明
template <typename T>
T add(T a, T b) { // 定义
return a + b;
}
// main.cpp - 正常使用
#include "add.h"
int main() {
add(1, 2); // 正确
}
解决方案二:显式实例化(放 .cpp)
cpp
// add.h
template <typename T>
T add(T a, T b); // 只声明
// add.cpp
template <typename T>
T add(T a, T b) { // 定义
return a + b;
}
// 显式实例化 - 告诉编译器要生成哪些版本
template int add<int>(int, int);
template double add<double>(double, double);
// main.cpp
#include "add.h"
int main() {
add(1, 2); // 可以用,因为 int 版本已生成
add(1.5, 2.5); // 可以用,因为 double 版本已生成
// add("a", "b"); // 错误!没有生成 string 版本
}
类的声明和定义分离
cpp
// Box.h
template <typename T>
class Box {
public:
Box(T val);
T get();
};
// Box.cpp
template <typename T>
Box<T>::Box(T val) : data(val) {} // 定义
template <typename T>
T Box<T>::get() { return data; } // 定义
// main.cpp - 链接错误!
正确做法一:全放头文件
cpp
// Box.h
template <typename T>
class Box {
T data;
public:
Box(T val) : data(val) {} // 直接定义
T get() { return data; } // 直接定义
};
正确做法二:头文件包含实现文件
cpp
// Box.h
template <typename T>
class Box {
T data;
public:
Box(T val);
T get();
};
#include "Box.tpp" // 包含实现文件
// Box.tpp(实现文件)
template <typename T>
Box<T>::Box(T val) : data(val) {}
template <typename T>
T Box<T>::get() { return data; }
正确做法三:显式实例化
cpp
// Box.h
template <typename T>
class Box {
T data;
public:
Box(T val);
T get();
};
// Box.cpp
template <typename T>
Box<T>::Box(T val) : data(val) {}
template <typename T>
T Box<T>::get() { return data; }
// 显式实例化
template class Box<int>;
template class Box<double>;
// main.cpp
#include "Box.h"
int main() {
Box<int> b1(10); // 可以用
Box<double> b2(3.14); // 可以用
// Box<string> b3("x"); // 错误!没有显式实例化
}
\[\]重载
cpp
template <typename T>
class MyVector {
T* data_;
public:
T& operator[](size_t i) {
return data_[i]; // 返回引用,不是副本
}
};
int main() {
MyVector<int> vec;
// 假设 data_ 指向 [1, 2, 3, ...]
// 这行代码发生了什么:
vec[0] = 10;
// 分解步骤:
// 1. vec[0] 调用 operator[](0)
// 2. operator[](0) 返回 data_[0] 的引用(不是副本)
// 3. 给这个引用赋值 = 10
// 4. 相当于直接修改 data_[0] = 10
}
常量
- 常量指针(const pointer)
指针本身是常量,不能改变指向,但可以修改指向的值
cpp
int x = 10;
int y = 20;
int* const p = &x; // 常量指针:p 本身是 const
*p = 30; // 正确!可以修改指向的值(x 变成 30)
// p = &y; // 错误!不能改变指针的指向
// 记忆:const 在 * 后面,修饰 p(指针本身)
- 指针常量(pointer to const)
指针指向的值是常量,不能通过指针修改值,但可以改变指向
cpp
int x = 10;
int y = 20;
const int* p = &x; // 指针常量:指向的值是 const
// *p = 30; // 错误!不能通过 p 修改 x
p = &y; // 正确!可以改变指针的指向
// 记忆:const 在 * 前面,修饰 *p(指向的值)
- 引用(没有"引用常量")
引用本身就不能改变指向,所以:
cpp
int x = 10;
int y = 20;
int& ref = x; // 普通引用
ref = y; // 这是给 x 赋值,不是改变 ref 的指向!
// 引用一旦绑定,就不能重新绑定
// 所以没有"引用常量"这种说法
- 常量引用(reference to const)
通过引用不能修改值
cpp
int x = 10;
const int& ref = x; // 常量引用
// ref = 30; // 错误!不能通过 ref 修改 x
int y = ref; // 正确!可以读取
// 没有 int& const ref = x; 这种写法(引用本身就不能改)
记忆技巧
cpp
// 从右往左读
int* const p; // p is a const pointer to int
const int* p; // p is a pointer to const int
const int* const p; // p is a const pointer to const int
const int& ref; // ref is a reference to const int
class 里面定义 struct
class 里面可以定义 struct,默认是 public 的。也可以定义 class(默认 private)
typedef
typedef typename CHxAllocator::template rebind::other NodeAlloc;
原类型:CHxAllocator::template rebind::other(很长的一串)
新类型(别名):NodeAlloc
意思:给 CHxAllocator::template rebind::other 这个类型起个简单的名字叫 NodeAlloc。
CHxAllocator::template rebind::other
CHxAllocator - 一个分配器类型
::rebind - 访问里面的 rebind 模板
::other - 访问 rebind 里的 other 类型
::
:: 是作用域解析运算符,用于访问类、命名空间、结构体等作用域里的成员。
cpp
namespace MyNamespace {
int x = 10;
}
class MyClass {
public:
typedef int value_type; // 类型成员
static int count; // 静态成员
struct Inner {
int y;
};
};
int main() {
// 命名空间
MyNamespace::x = 20;
// 类的类型成员
MyClass::value_type val = 10;
// 类的静态成员
MyClass::count = 5;
// 类里的结构体
MyClass::Inner inner;
inner.y = 30;
}
throw std::bad_alloc(0);
cpp
throw 异常对象; // 抛出异常
// std::bad_alloc 是标准库的异常类
// std::bad_alloc(0) 是创建一个 bad_alloc 对象
// throw 把这个异常抛出去