1. 面向对象编程基础
面向对象编程(OOP)是现代软件开发中最主流的编程范式之一。与传统的面向过程编程相比,OOP将数据和操作数据的方法封装在一起,形成"对象"的概念,更贴近现实世界的思维方式。
1.1 面向对象三大特性
封装是OOP最基础也最重要的特性。它有两层含义:
- 将数据和行为捆绑在一起,形成一个独立的单元
- 对外隐藏内部实现细节,只暴露必要的接口
在实际编码中,我们通过访问控制符(public/private/protected)来实现封装。例如银行账户的余额应该设为private,只能通过特定的公开方法(deposit/withdraw)来修改:
cpp复制class BankAccount {
private:
double balance; // 私有成员,外部无法直接访问
public:
void deposit(double amount) {
if (amount > 0) balance += amount;
}
bool withdraw(double amount) {
if (amount <= balance) {
balance -= amount;
return true;
}
return false;
}
};
继承允许我们基于现有类创建新类,实现代码复用和层次化设计。派生类会自动获得基类的属性和方法,同时可以添加新的特性或修改现有行为。
多态则让不同类的对象可以对同一消息做出不同响应。在C++中主要通过虚函数和函数重载实现。
1.2 面向过程 vs 面向对象对比
| 特性 | 面向过程 | 面向对象 |
|---|---|---|
| 基本单元 | 函数 | 对象 |
| 数据与行为关系 | 分离 | 绑定 |
| 代码复用方式 | 函数调用 | 继承/组合 |
| 典型应用场景 | 简单算法、脚本 | 复杂系统、GUI、业务逻辑 |
| 维护性 | 修改可能影响多处 | 修改通常局限在类内部 |
| 扩展性 | 需要修改现有代码 | 可通过继承扩展 |
实际开发中,两种范式并非互斥。C++作为多范式语言,可以灵活结合两者优势。通常建议:核心业务逻辑采用面向对象设计,底层算法和性能敏感部分可采用面向过程方式。
1.3 常见误区与最佳实践
初学者常犯的几个错误:
-
过度暴露实现细节
- 错误做法:将所有成员变量设为public
- 正确做法:遵循"最小权限原则",只暴露必要的接口
-
贫血模型
- 错误做法:类中只有数据没有行为
- 正确做法:将相关操作封装在类中,保持高内聚
-
忽视封装的价值
- 错误做法:认为private/public是多余的语法负担
- 正确做法:封装可以降低耦合,提高可维护性
一个良好的类设计应该像"黑盒子":使用者只需知道它能做什么(接口),而不需要关心如何实现(内部细节)。这种抽象层次让代码更易于理解、维护和扩展。
2. 类的定义与实现
2.1 基本类定义语法
在C++中,类通过class关键字定义,基本结构如下:
cpp复制class ClassName {
access_specifier:
member_variables;
member_functions();
};
一个完整的Circle类示例:
cpp复制#include <iostream>
#include <cmath> // 使用M_PI常量
class Circle {
private:
double radius;
public:
// 设置半径(带参数校验)
void setRadius(double r) {
if (r >= 0) radius = r;
else std::cerr << "半径不能为负" << std::endl;
}
// 获取面积
double getArea() const {
return M_PI * radius * radius;
}
// 获取周长
double getCircumference() const {
return 2 * M_PI * radius;
}
// 打印圆的信息
void print() const {
std::cout << "半径: " << radius
<< ", 面积: " << getArea()
<< ", 周长: " << getCircumference() << std::endl;
}
};
2.2 类与结构体的区别
C++中class和struct本质相同,只有两个细微差别:
-
默认访问权限:
- class成员默认private
- struct成员默认public
-
继承时的默认访问权限:
- class继承默认private
- struct继承默认public
实际使用建议:
- 仅包含数据的简单结构使用struct
- 需要封装和行为的复杂类型使用class
cpp复制// 作为数据容器使用
struct Point {
double x, y;
};
// 具有行为的类
class Circle {
Point center;
double radius;
public:
double area() const { /*...*/ }
};
2.3 成员函数的定义方式
成员函数可以在类内定义(自动成为inline),也可以在类外定义:
cpp复制// 头文件 Rectangle.h
class Rectangle {
private:
double width, height;
public:
// 类内定义(隐式inline)
void setWidth(double w) { width = w; }
// 仅声明
void setHeight(double h);
double area() const;
};
// 源文件 Rectangle.cpp
#include "Rectangle.h"
// 类外定义
void Rectangle::setHeight(double h) {
height = h;
}
double Rectangle::area() const {
return width * height;
}
实际工程中,简单的getter/setter可以在类内定义,复杂逻辑建议放在源文件中实现,以保持头文件简洁。
3. 访问控制与封装
3.1 访问控制符详解
C++提供三种访问控制级别:
- public:在任何地方都可访问
- private:仅类自身成员函数可访问
- protected:类自身和派生类可访问
一个Person类的典型示例:
cpp复制class Person {
private:
std::string idNumber; // 私有,外部无法访问
protected:
std::string address; // 派生类可访问
public:
std::string name; // 完全公开
// 通过公开方法访问私有数据
void setIdNumber(const std::string& id) {
if (isValidId(id)) idNumber = id;
}
std::string getIdNumber() const {
return maskIdNumber(idNumber); // 返回脱敏后的ID
}
};
3.2 封装的实际价值
良好的封装带来以下优势:
- 数据保护:防止非法修改(如银行账户余额)
- 接口稳定性:内部实现可自由修改而不影响使用者
- 使用简化:隐藏复杂实现细节
- 错误预防:通过校验逻辑保证数据一致性
一个更完善的银行账户实现:
cpp复制class BankAccount {
private:
double balance;
std::string accountNumber;
// 私有校验函数
bool isValidAmount(double amount) const {
return amount > 0 && amount <= 1000000; // 假设单笔交易上限100万
}
public:
BankAccount(const std::string& num, double initial = 0)
: accountNumber(num), balance(initial) {}
bool deposit(double amount) {
if (!isValidAmount(amount)) return false;
balance += amount;
return true;
}
bool withdraw(double amount) {
if (!isValidAmount(amount) || amount > balance) return false;
balance -= amount;
return true;
}
double getBalance() const { return balance; }
const std::string& getAccountNumber() const { return accountNumber; }
};
3.3 设计原则:Law of Demeter
迪米特法则(最少知识原则)建议:
- 每个对象应该只与其"朋友"交流(直接成员、参数、创建的对象等)
- 不要与"陌生"对象交流
违反迪米特的代码:
cpp复制// 不好的写法:穿透多层访问
void printUserAddress(Database db, int userId) {
std::cout << db.getUsers().get(userId).getAddress().toString();
}
遵循迪米特的改进:
cpp复制// 好的写法:委托给User类处理
void printUserAddress(Database db, int userId) {
std::cout << db.getUserAddress(userId);
}
4. 成员函数进阶
4.1 const成员函数
const成员函数承诺不修改对象状态,重要特性:
- 可以被const对象调用
- 不能修改成员变量(除非mutable)
- 不能调用非const成员函数
cpp复制class TextBlock {
private:
std::string text;
mutable int accessCount; // 即使const函数也可修改
public:
// const版本
const char& operator[](size_t pos) const {
accessCount++;
return text[pos];
}
// 非const版本
char& operator[](size_t pos) {
return text[pos];
}
};
4.2 函数重载与const
const可以作为函数重载的依据,编译器会根据对象constness选择合适版本:
cpp复制TextBlock tb("Hello");
const TextBlock ctb("World");
tb[0] = 'h'; // 调用非const版本
char c = ctb[0]; // 调用const版本
4.3 内联函数
类内定义的成员函数自动成为inline候选,适合简单函数:
cpp复制class Vector {
private:
double x, y;
public:
// 隐式inline
void setX(double val) { x = val; }
void setY(double val) { y = val; }
// 显式inline声明
inline double length() const;
};
// 定义处仍需inline关键字
inline double Vector::length() const {
return std::sqrt(x*x + y*y);
}
现代编译器会自动决定是否内联,过度使用inline可能导致代码膨胀。
5. this指针深入解析
5.1 this的本质
this是类的非静态成员函数的隐式参数,指向调用该成员函数的对象。关键点:
- 类型为
ClassName* const(常量指针) - 在const成员函数中为
const ClassName* const - 静态成员函数没有this指针
cpp复制class MyClass {
int data;
public:
void setData(int data) {
this->data = data; // 解决命名冲突
}
MyClass& getThis() {
return *this; // 返回对象引用
}
};
5.2 链式调用技巧
通过返回*this可以实现方法链:
cpp复制class Printer {
public:
Printer& print(const std::string& msg) {
std::cout << msg;
return *this;
}
Printer& endl() {
std::cout << std::endl;
return *this;
}
};
// 使用示例
Printer().print("Hello").print(" ").print("World").endl();
5.3 实现赋值运算符
this指针在运算符重载中尤为重要:
cpp复制class Widget {
int* data;
public:
Widget& operator=(const Widget& rhs) {
if (this != &rhs) { // 自赋值检查
delete data;
data = new int(*rhs.data);
}
return *this;
}
};
6. 静态成员
6.1 静态成员变量
静态成员变量特点:
- 属于类而非对象
- 需要在类外定义(恰好一次)
- 所有对象共享同一实例
cpp复制class Employee {
private:
static int count; // 声明
std::string name;
public:
Employee(const std::string& n) : name(n) { count++; }
~Employee() { count--; }
static int getCount() { return count; }
};
// 定义(通常在源文件中)
int Employee::count = 0;
6.2 静态成员函数
静态成员函数特点:
- 没有this指针
- 只能访问静态成员
- 通过类名调用
cpp复制class MathUtils {
public:
static double pi() { return 3.141592653589793; }
static int max(int a, int b) {
return a > b ? a : b;
}
};
// 使用
double circleArea = MathUtils::pi() * radius * radius;
6.3 单例模式实现
静态成员常用于实现单例:
cpp复制class Logger {
private:
static Logger instance;
Logger() {} // 私有构造函数
public:
static Logger& getInstance() {
return instance;
}
void log(const std::string& msg) {
// 记录日志
}
};
// 定义静态成员
Logger Logger::instance;
// 使用
Logger::getInstance().log("System started");
7. 友元机制
7.1 友元函数
友元函数可以访问类的私有成员,常用于:
- 运算符重载
- 需要特殊访问权限的工具函数
cpp复制class Matrix {
private:
double data[4][4];
public:
friend Matrix multiply(const Matrix& a, const Matrix& b);
};
Matrix multiply(const Matrix& a, const Matrix& b) {
Matrix result;
// 直接访问私有数据
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
result.data[i][j] = 0;
for (int k = 0; k < 4; ++k) {
result.data[i][j] += a.data[i][k] * b.data[k][j];
}
}
}
return result;
}
7.2 友元类
友元类中的所有成员函数都可以访问授予类的私有成员:
cpp复制class Storage {
private:
int secretCode;
friend class Security; // 授权访问
};
class Security {
public:
static bool checkCode(const Storage& s, int code) {
return s.secretCode == code; // 访问私有成员
}
};
7.3 友元使用建议
虽然友元破坏了封装,但在某些场景下是必要的:
- 紧密耦合的类(如容器和迭代器)
- 运算符重载(如<<, >>)
- 单元测试(测试类作为被测类的友元)
应谨慎使用友元,优先考虑设计更好的接口。过度使用友元通常意味着类设计存在问题。
8. 综合实践案例
8.1 日期类实现
一个完整的日期类,包含日期计算和校验:
cpp复制#include <iostream>
#include <stdexcept>
class Date {
private:
int year;
int month;
int day;
bool isLeapYear() const {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
int daysInMonth() const {
constexpr int days[] = {31,28,31,30,31,30,31,31,30,31,30,31};
if (month == 2 && isLeapYear()) return 29;
return days[month-1];
}
void validate() {
if (year < 1900 || year > 2100 ||
month < 1 || month > 12 ||
day < 1 || day > daysInMonth()) {
throw std::invalid_argument("Invalid date");
}
}
public:
Date(int y, int m, int d) : year(y), month(m), day(d) {
validate();
}
Date& addDays(int days) {
while (days > 0) {
int remaining = daysInMonth() - day + 1;
if (days >= remaining) {
days -= remaining;
day = 1;
if (++month > 12) {
month = 1;
year++;
}
} else {
day += days;
days = 0;
}
}
return *this;
}
void print() const {
std::cout << year << "-" << month << "-" << day << std::endl;
}
// 比较运算符
friend bool operator==(const Date& lhs, const Date& rhs);
friend bool operator<(const Date& lhs, const Date& rhs);
};
bool operator==(const Date& lhs, const Date& rhs) {
return lhs.year == rhs.year &&
lhs.month == rhs.month &&
lhs.day == rhs.day;
}
bool operator<(const Date& lhs, const Date& rhs) {
if (lhs.year != rhs.year) return lhs.year < rhs.year;
if (lhs.month != rhs.month) return lhs.month < rhs.month;
return lhs.day < rhs.day;
}
8.2 银行账户系统
扩展版的银行账户系统,包含账户管理和交易记录:
cpp复制#include <iostream>
#include <vector>
#include <string>
#include <ctime>
class Transaction {
public:
enum Type { DEPOSIT, WITHDRAW, TRANSFER };
private:
Type type;
double amount;
std::time_t timestamp;
std::string description;
public:
Transaction(Type t, double amt, const std::string& desc = "")
: type(t), amount(amt), timestamp(std::time(nullptr)), description(desc) {}
void print() const {
std::cout << "[" << std::ctime(×tamp) << "] ";
switch(type) {
case DEPOSIT: std::cout << "存款"; break;
case WITHDRAW: std::cout << "取款"; break;
case TRANSFER: std::cout << "转账"; break;
}
std::cout << ": " << amount;
if (!description.empty()) {
std::cout << " (" << description << ")";
}
std::cout << std::endl;
}
};
class BankAccount {
private:
std::string accountNumber;
std::string owner;
double balance;
std::vector<Transaction> transactions;
public:
BankAccount(const std::string& num, const std::string& own, double init = 0)
: accountNumber(num), owner(own), balance(init) {}
bool deposit(double amount, const std::string& desc = "") {
if (amount <= 0) return false;
balance += amount;
transactions.emplace_back(Transaction::DEPOSIT, amount, desc);
return true;
}
bool withdraw(double amount, const std::string& desc = "") {
if (amount <= 0 || amount > balance) return false;
balance -= amount;
transactions.emplace_back(Transaction::WITHDRAW, amount, desc);
return true;
}
bool transfer(BankAccount& to, double amount, const std::string& desc = "") {
if (amount <= 0 || amount > balance) return false;
balance -= amount;
to.balance += amount;
transactions.emplace_back(Transaction::TRANSFER, amount, desc + "→" + to.owner);
to.transactions.emplace_back(Transaction::TRANSFER, amount, desc + "←" + owner);
return true;
}
void printStatement() const {
std::cout << "账户: " << accountNumber
<< "\n户主: " << owner
<< "\n余额: " << balance << "\n\n交易记录:\n";
for (const auto& t : transactions) {
t.print();
}
}
};
9. 设计原则与最佳实践
9.1 SOLID原则在类设计中的应用
-
单一职责原则(SRP)
- 一个类应该只有一个改变的理由
- 避免"上帝类",将不同功能拆分到不同类中
-
开闭原则(OCP)
- 对扩展开放,对修改关闭
- 使用继承和多态实现扩展
-
里氏替换原则(LSP)
- 派生类应该能够替换基类而不影响程序正确性
- 避免在派生类中削弱前置条件或强化后置条件
-
接口隔离原则(ISP)
- 客户端不应被迫依赖它们不使用的接口
- 将大接口拆分为更小、更具体的接口
-
依赖倒置原则(DIP)
- 高层模块不应依赖低层模块,两者都应依赖抽象
- 抽象不应依赖细节,细节应依赖抽象
9.2 类设计检查清单
设计类时应考虑以下问题:
- 这个类的职责是否明确单一?
- 公开接口是否最小化?
- 是否隐藏了实现细节?
- 是否考虑了const正确性?
- 资源管理是否明确(谁拥有资源)?
- 是否考虑了异常安全性?
- 是否支持移动语义(对于资源管理类)?
- 是否提供了适当的拷贝控制(拷贝构造、赋值、析构)?
- 类接口是否易于正确使用而难以误用?
- 是否避免了不必要的友元关系?
9.3 性能考量
- 对象大小:避免过大对象,考虑将部分数据移到堆上
- 内联小函数:简单的getter/setter适合内联
- 缓存友好:合理安排成员变量顺序,提高局部性
- 避免虚函数滥用:虚函数调用有额外开销
- 返回值优化(RVO):通过返回局部对象利用编译器优化
cpp复制// 利用RVO的例子
Matrix operator+(const Matrix& a, const Matrix& b) {
Matrix result(a); // 局部对象
result += b;
return result; // 编译器可能避免拷贝
}
10. 常见问题与解决方案
10.1 头文件组织
良好的头文件应包含:
- 类定义
- 内联函数实现
- 必要的类型别名
- 相关常量定义
示例头文件结构:
cpp复制// MyClass.h
#ifndef MYCLASS_H
#define MYCLASS_H
#include <string>
#include <vector>
class MyClass {
public:
using StringList = std::vector<std::string>; // 类型别名
MyClass(); // 构造函数
explicit MyClass(int initVal); // 避免隐式转换
void publicMethod();
int getValue() const { return value; } // 内联实现
private:
int value;
StringList data;
void privateMethod();
};
// 内联函数实现(如果较复杂)
inline void MyClass::privateMethod() {
// 实现
}
#endif // MYCLASS_H
10.2 循环依赖问题
当两个类互相引用时会产生循环依赖,解决方案:
- 前向声明
- 将依赖移到源文件中
- 重新设计消除循环
cpp复制// 方案1:前向声明
class B; // 前向声明
class A {
B* b_ptr;
};
class B {
A* a_ptr;
};
10.3 常量正确性
const正确性指南:
- 默认将成员函数声明为const
- 按需使用mutable修饰可能被const函数修改的成员
- 参数传递:输入参数用const引用,输出参数用指针
- 返回值:除非需要修改,否则返回const值
cpp复制class Text {
mutable size_t accessCount; // const函数可修改
std::string content;
public:
const char& operator[](size_t pos) const {
accessCount++;
return content[pos];
}
char& operator[](size_t pos) {
return content[pos];
}
};
10.4 对象生命周期管理
常见问题及解决方案:
- 悬空指针:使用智能指针(unique_ptr/shared_ptr)
- 资源泄漏:遵循RAII原则
- 对象切片:传递多态对象时使用指针或引用
- 静态初始化顺序问题:使用局部静态变量或构造时初始化
RAII示例:
cpp复制class FileHandle {
FILE* file;
public:
explicit FileHandle(const char* filename)
: file(fopen(filename, "r")) {
if (!file) throw std::runtime_error("File open failed");
}
~FileHandle() {
if (file) fclose(file);
}
// 禁用拷贝
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
// 启用移动
FileHandle(FileHandle&& other) noexcept : file(other.file) {
other.file = nullptr;
}
FileHandle& operator=(FileHandle&& other) noexcept {
if (this != &other) {
if (file) fclose(file);
file = other.file;
other.file = nullptr;
}
return *this;
}
void read(/*...*/) { /*...*/ }
};
11. 现代C++特性与类设计
11.1 默认和删除函数
C++11允许显式控制特殊成员函数的生成:
cpp复制class NonCopyable {
public:
NonCopyable() = default;
// 禁用拷贝
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
// 允许移动
NonCopyable(NonCopyable&&) = default;
NonCopyable& operator=(NonCopyable&&) = default;
};
11.2 override和final
明确派生类中的虚函数关系:
cpp复制class Base {
public:
virtual void foo() const;
virtual void bar() final; // 禁止派生类覆盖
};
class Derived : public Base {
public:
void foo() const override; // 明确表示覆盖
// void bar(); // 错误:基类中已final
};
11.3 移动语义与类设计
为资源管理类实现移动操作:
cpp复制class Buffer {
char* data;
size_t size;
public:
// 移动构造函数
Buffer(Buffer&& other) noexcept
: data(other.data), size(other.size) {
other.data = nullptr;
other.size = 0;
}
// 移动赋值运算符
Buffer& operator=(Buffer&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
size = other.size;
other.data = nullptr;
other.size = 0;
}
return *this;
}
// ... 其他成员 ...
};
11.4 委托构造函数
C++11引入的构造函数复用机制:
cpp复制class Time {
int hour, minute, second;
public:
Time() : Time(0, 0, 0) {} // 委托给三参数构造函数
Time(int h) : Time(h, 0, 0) {} // 委托
Time(int h, int m) : Time(h, m, 0) {} // 委托
Time(int h, int m, int s) // 目标构造函数
: hour(h), minute(m), second(s) {
normalize();
}
};
12. 设计模式与类设计
12.1 工厂模式
通过静态方法创建对象,隐藏具体实现:
cpp复制class Shape {
public:
virtual ~Shape() = default;
virtual void draw() const = 0;
// 工厂方法
static std::unique_ptr<Shape> create(const std::string& type);
};
class Circle : public Shape {
public:
void draw() const override { /*...*/ }
};
class Rectangle : public Shape {
public:
void draw() const override { /*...*/ }
};
std::unique_ptr<Shape> Shape::create(const std::string& type) {
if (type == "circle") return std::make_unique<Circle>();
if (type == "rectangle") return std::make_unique<Rectangle>();
throw std::invalid_argument("Unknown shape type");
}
12.2 观察者模式
实现对象间的一对多依赖:
cpp复制#include <vector>
#include <functional>
class Subject {
std::vector<std::function<void()>> observers;
public:
void attach(std::function<void()> observer) {
observers.push_back(observer);
}
void notify() {
for (auto& obs : observers) {
obs();
}
}
};
class TemperatureSensor : public Subject {
double temperature;
public:
void setTemperature(double temp) {
temperature = temp;
notify(); // 通知所有观察者
}
};
12.3 策略模式
运行时选择算法:
cpp复制class SortStrategy {
public:
virtual ~SortStrategy() = default;
virtual void sort(std::vector<int>&) const = 0;
};
class QuickSort : public SortStrategy {
public:
void sort(std::vector<int>& data) const override { /*...*/ }
};
class MergeSort : public SortStrategy {
public:
void sort(std::vector<int>& data) const override { /*...*/ }
};
class Sorter {
std::unique_ptr<SortStrategy> strategy;
public:
explicit Sorter(std::unique_ptr<SortStrategy> strat)
: strategy(std::move(strat)) {}
void sort(std::vector<int>& data) const {
strategy->sort(data);
}
};
13. 测试与调试技巧
13.1 单元测试类设计
为类编写可测试代码的建议:
- 依赖注入:通过构造函数或setter注入依赖
- 接口隔离:将测试点分散到小接口
- 虚函数钩子:为测试提供扩展点
- 测试友元:将测试类声明为被测类的友元
cpp复制class Database {
public:
virtual ~Database() = default;
virtual void save(const std::string& key, const std::string& value) = 0;
};
class UserService {
Database& db;
public:
explicit UserService(Database& db) : db(db) {}
void registerUser(const std::string& username) {
db.save("users/" + username, "registered");
}
};
// 测试用mock
class MockDatabase : public Database {
public:
std::string lastKey, lastValue;
void save(const std::string& key, const std::string& value) override {
lastKey = key;
lastValue = value;
}
};
// 测试用例
void testUserRegistration() {
MockDatabase mock;
UserService service(mock);
service.registerUser("testuser");
assert(mock.lastKey == "users/testuser");
assert(mock.lastValue == "registered");
}
13.2 调试技巧
- 打印对象状态:重载operator<<
- 运行时类型信息:使用typeid
- const检查:临时移除const看是否编译
- 对象布局检查:使用offsetof宏
cpp复制#include <typeinfo>
#include <cstddef>
class Debuggable {
public:
virtual ~Debuggable() = default;
friend std::ostream& operator<<(std::ostream& os, const Debuggable& obj) {
obj.debugPrint(os);
return os;
}
virtual void debugPrint(std::ostream& os) const {
os << typeid(*this).name() << " @ " << this;
}
};
class MyClass : public Debuggable {
int x, y;
public:
void debugPrint(std::ostream& os) const override {
Debuggable::debugPrint(os);
os << " x=" << x << " y=" << y;
}
};
// 使用
MyClass obj;
std::cout << obj << std::endl; // 输出完整状态
14. 性能优化技巧
14.1 对象池模式
减少频繁创建销毁对象的开销:
cpp复制#include <vector>
#include <memory>
template <typename T>
class ObjectPool {
std::vector<std::unique_ptr<T>> pool;
public:
template <typename... Args>
T* acquire(Args&&... args) {
if (pool.empty()) {
return new T(std::forward<Args>(args)...);
}
auto obj = pool.back().release();
pool.pop_back();
new (obj) T(std::forward<Args>(args)...); // 原位构造
return obj;
}
void release(T* obj) {
obj->~T(); // 显式析构
pool.emplace_back(obj);
}
};
14.2 小对象优化
避免小对象的内存分配开销:
cpp复制#include <new> // std::launder
class SmallString {
static constexpr size_t BufferSize = 16;
union {
char small[BufferSize];
char* large;
};
size_t size;
bool isSmall() const { return size <= BufferSize; }
public:
SmallString(const char* str) : size(strlen(str)) {
if (isSmall()) {
memcpy(small, str, size + 1);
} else {
large = new char[size + 1];
memcpy(large, str, size + 1);
}
}
~SmallString() {
if (!isSmall()) delete[] large;
}
const char* c_str() const {
return isSmall() ? small : large;
}
};
14.3 热路径优化
对性能关键代码的特殊处理:
- 避免虚函数调用:使用CRTP模式
- 内存局部性:紧凑存储数据
- 分支预测:标记likely/unlikely
cpp复制template <typename Derived>
class Base {
public:
void interface() {
static_cast<Derived*>(this)->implementation();
}
};
class Derived : public Base<Derived> {
public:
void implementation() {
// 直接调用,无虚函数开销
}
};
// 使用
Derived d;
d.interface(); // 静态分派
15. 跨平台注意事项
15.1 数据对齐
处理不同平台的对齐要求:
cpp复制#include <type_traits>
template <typename T>
class AlignedAllocator {
public:
static constexpr size_t alignment = alignof(T);
static T* allocate(size_t n) {
if (n > std::numeric_limits<size_t>::max() / sizeof(T)) {
throw std::bad_alloc();
}
void* ptr;
#ifdef _WIN32
ptr = _aligned_malloc(n * sizeof(T), alignment);
#else
if (posix_memalign(&ptr, alignment, n * sizeof(T)) != 0) {
ptr = nullptr;
}
#endif
if (!ptr) throw std::bad_alloc();
return static_cast<T*>(ptr);
}
static void deallocate(T* p) noexcept {
#ifdef _WIN32
_aligned_free(p);
#else
free(p);
#endif
}
};
15.2 字节序处理
处理不同平台的字节序问题:
cpp复制#include <cstdint>
#include <bit>
class NetworkBuffer {
uint8_t* data;
size_t size;
public:
template <typename T>
T read(size_t offset) const {
static_assert(std::is_arithmetic_v<T>, "Only arithmetic types