1. 初始化列表深度解析
在C++中,初始化列表是构造函数的重要组成部分,它决定了类成员变量的初始化方式。理解初始化列表的工作机制对于编写健壮的C++代码至关重要。
1.1 必须使用初始化列表的三种情况
当类成员变量属于以下三种类型时,必须在初始化列表中进行初始化:
-
const修饰的成员变量
cpp复制class Example { public: Example(int val) : constValue(val) {} // 必须在初始化列表初始化 private: const int constValue; }; -
引用类型的成员变量
cpp复制class RefHolder { public: RefHolder(int& ref) : refMember(ref) {} // 引用必须在初始化列表绑定 private: int& refMember; }; -
没有默认构造函数的自定义类型成员
cpp复制class NoDefault { public: NoDefault(int x) {} // 只有带参构造函数,没有默认构造 }; class Container { public: Container() : member(42) {} // 必须显式初始化 private: NoDefault member; };
重要提示:这三种情况如果不在初始化列表初始化,会导致编译错误。即使C++11允许在声明时给缺省值,对于这三种特殊情况仍然必须使用初始化列表。
1.2 初始化列表的工作原理
初始化列表的本质是成员变量的定义和初始化位置。每个成员变量在初始化列表中只能出现一次,编译器会按照成员变量在类中的声明顺序(而非初始化列表中的书写顺序)进行初始化。
cpp复制class InitializationOrder {
public:
// 看似先初始化a,再初始化b,实际按声明顺序b先初始化
InitializationOrder(int val)
: a(val), b(a) {} // 危险!b可能使用未初始化的a
private:
int b; // 先声明
int a; // 后声明
};
1.3 C++11的成员变量缺省值
C++11允许在声明成员变量时直接给出缺省值,这个特性与初始化列表的关系需要特别注意:
cpp复制class DefaultValues {
public:
DefaultValues() {} // 使用缺省值初始化x和y
DefaultValues(int a) : x(a) {} // 只覆盖x的缺省值
private:
int x = 10; // 声明时给缺省值
int y = 20;
};
初始化列表的优先级高于声明时的缺省值。当两者同时存在时,初始化列表中的值会覆盖声明时的缺省值。
1.4 初始化列表的最佳实践
-
统一使用初始化列表:即使不是必须使用初始化列表的情况,也建议使用初始化列表初始化所有成员变量。
-
保持声明顺序与初始化顺序一致:避免因初始化顺序导致的隐蔽错误。
-
对于const、引用和无默认构造的成员:必须使用初始化列表,这是硬性要求。
-
合理使用C++11缺省值:对于非必须使用初始化列表的成员,可以使用声明时缺省值简化代码。
2. 类型转换机制详解
C++中的类型转换是一个强大但需要谨慎使用的特性,理解各种转换场景对于编写安全的代码非常重要。
2.1 内置类型到类类型的隐式转换
当类定义了接受单一参数的构造函数时,编译器可以自动进行类型转换:
cpp复制class Date {
public:
Date(int year) : year_(year) {} // 转换构造函数
private:
int year_;
};
void printDate(const Date& d);
// 使用
printDate(2023); // 隐式转换:int → Date
这种转换有时会导致意外的行为,因此C++提供了explicit关键字来禁止隐式转换。
2.2 explicit关键字的作用
通过在构造函数前添加explicit,可以防止编译器进行隐式转换:
cpp复制class ExplicitDate {
public:
explicit ExplicitDate(int year) : year_(year) {}
private:
int year_;
};
void printExplicitDate(const ExplicitDate& d);
// 使用
// printExplicitDate(2023); // 错误:不能隐式转换
printExplicitDate(ExplicitDate(2023)); // 必须显式构造
2.3 多参数构造函数的隐式转换
C++11扩展了隐式转换的概念,支持多参数构造函数的列表初始化:
cpp复制class MultiParam {
public:
MultiParam(int x, double y) : x_(x), y_(y) {}
private:
int x_;
double y_;
};
// 使用
MultiParam m = {1, 3.14}; // 多参数隐式转换
同样,可以在多参数构造函数前加explicit来禁止这种隐式转换。
2.4 类类型之间的转换
一个类可以定义接受另一个类类型作为参数的构造函数,从而实现类类型之间的转换:
cpp复制class Time {
// Time类定义
};
class DateTime {
public:
DateTime(const Time& t) { /* 转换逻辑 */ }
};
// 使用
Time t;
DateTime dt = t; // Time → DateTime的隐式转换
2.5 类型转换的注意事项
-
谨慎使用隐式转换:虽然方便,但可能导致代码难以理解和维护。
-
优先使用explicit:除非有充分理由,否则应该将单参数构造函数声明为explicit。
-
注意转换开销:隐式转换可能产生临时对象,影响性能。
-
保持转换明确:如果转换可能引起歧义,应该提供更明确的转换接口。
3. static成员全面剖析
static成员是类的重要组成部分,它们与普通成员变量和函数有本质区别。
3.1 static成员变量特性
static成员变量属于类本身,而不是类的任何特定对象:
cpp复制class Counter {
public:
Counter() { ++count_; }
~Counter() { --count_; }
static int getCount() { return count_; }
private:
static int count_; // 声明
};
// 定义和初始化
int Counter::count_ = 0; // 必须在类外初始化
关键特点:
- 所有对象共享同一个static变量
- 存储在静态存储区而非对象内存中
- 必须在类外进行定义和初始化
3.2 static成员函数特性
static成员函数不依赖于特定对象,没有this指针:
cpp复制class MathUtils {
public:
static double square(double x) { return x * x; }
// 不能访问非static成员
// static void error() { cout << value; } // 错误
private:
int value; // 非static成员
};
使用方式:
cpp复制double result = MathUtils::square(4.2); // 通过类名调用
3.3 static成员的访问控制
static成员同样受访问限定符约束:
cpp复制class AccessControl {
public:
static publicStatic() {}
private:
static privateStatic() {}
static int privateVar;
};
3.4 static成员的应用场景
- 共享数据:如计数器、共享配置等
- 工具函数:不需要对象状态的实用函数
- 单例模式:控制类只有一个实例
- 类常量:使用const static表示类级别的常量
3.5 注意事项
- 初始化顺序问题:不同编译单元的static变量初始化顺序不确定
- 线程安全问题:多线程环境下需要额外保护
- 内存管理:static变量生命周期直到程序结束
- 避免滥用:过度使用static会导致代码耦合度高
4. 友元机制深入探讨
友元是C++中突破封装性的特殊机制,需要谨慎使用。
4.1 友元函数
友元函数可以访问类的私有成员,但不是类的成员函数:
cpp复制class Box {
friend void printBox(const Box& b); // 友元声明
private:
double width;
};
// 定义
void printBox(const Box& b) {
cout << b.width; // 可以访问私有成员
}
特点:
- 可以在类的任何区域声明(public/protected/private)
- 没有this指针
- 一个函数可以是多个类的友元
4.2 友元类
一个类可以将另一个类声明为友元,使其所有成员函数都能访问自己的私有成员:
cpp复制class Storage {
friend class Cleaner; // 友元类声明
private:
int secretCode;
};
class Cleaner {
public:
void clean(Storage& s) {
s.secretCode = 0; // 可以访问Storage的私有成员
}
};
特点:
- 单向性:A是B的友元,不意味着B是A的友元
- 不传递:A是B的友元,B是C的友元,不意味着A是C的友元
- 继承无关:友元关系不继承
4.3 友元的合理使用场景
- 运算符重载:特别是流操作符<<和>>
- 需要紧密协作的类:如容器和迭代器
- 测试代码:单元测试中访问私有成员进行验证
- 性能关键代码:避免getter/setter的开销
4.4 友元的替代方案
在可能的情况下,优先考虑以下替代方案:
- 提供适当的公有接口
- 使用嵌套类
- 重新设计类关系
4.5 友元使用准则
- 最小化原则:只授予必要的访问权限
- 文档化:明确说明为什么需要友元
- 避免循环友元:会导致高度耦合
- 考虑可维护性:友元关系增加了修改的难度
5. 内部类精讲
内部类是定义在另一个类内部的类,具有特殊的访问权限。
5.1 基本语法
cpp复制class Outer {
public:
class Inner { // 内部类定义
public:
void accessOuter(const Outer& o) {
cout << o.privateData; // 可以访问Outer的私有成员
}
};
private:
int privateData;
};
5.2 内部类的特点
- 默认友元关系:内部类可以访问外部类的所有成员
- 空间独立:内部类不占用外部类的存储空间
- 访问控制:内部类可以放在public/protected/private区域
- 作用域限定:外部需要使用Outer::Inner来引用内部类
5.3 内部类的使用场景
- 实现细节隐藏:将只在外部类中使用的辅助类作为内部类
- 迭代器模式:容器类中的迭代器通常实现为内部类
- 回调机制:特定于外部类的回调实现
- 命名空间管理:避免污染全局命名空间
5.4 内部类与外部类的关系
-
创建依赖:内部类可以独立创建,不需要外部类实例
cpp复制Outer::Inner innerObj; // 直接创建内部类对象 -
访问规则:
- 内部类可以直接访问外部类的static成员
- 访问外部类的非static成员需要通过对象实例
5.5 内部类的设计考量
- 封装性:将紧密相关的类组织在一起
- 可读性:明确表示类之间的主从关系
- 访问控制:通过放在不同区域控制内部类的可见性
- 编译依赖:减少头文件间的相互引用
6. 匿名对象与编译器优化
匿名对象和编译器优化是影响C++性能的重要特性。
6.1 匿名对象基础
匿名对象是没有名称的临时对象,生命周期仅限于创建它的表达式:
cpp复制class Temp {
public:
Temp() { cout << "构造\n"; }
~Temp() { cout << "析构\n"; }
};
// 使用
Temp(); // 创建匿名对象,立即析构
6.2 匿名对象的典型用途
-
临时函数调用:
cpp复制class Logger { public: static void log(const string& msg); }; Logger().log("临时消息"); // 匿名对象调用成员函数 -
函数参数传递:
cpp复制void process(const Data& d); process(Data(42)); // 传递匿名对象 -
返回值优化:
cpp复制Data createData() { return Data(100); // 可能触发返回值优化 }
6.3 编译器优化技术
C++编译器会尽可能消除不必要的对象拷贝:
-
返回值优化(RVO):消除返回临时对象的拷贝
cpp复制Data makeData() { return Data(); // 可能直接构造在调用处 } -
具名返回值优化(NRVO):消除返回局部变量的拷贝
cpp复制Data makeData() { Data d; return d; // 可能直接使用调用处的存储 } -
拷贝消除:合并连续的构造和拷贝操作
cpp复制Data d = Data(10); // 可能直接构造d,不生成临时对象
6.4 优化场景对比
| 场景 | 无优化 | 基本优化 | 激进优化 |
|---|---|---|---|
Data d = Data(10); |
构造临时对象+拷贝构造 | 直接构造d | 直接构造d |
Data d = makeData(); |
构造+拷贝+拷贝 | 构造+拷贝 | 直接构造 |
d = makeData(); |
构造+拷贝+赋值 | 构造+赋值 | 构造+赋值 |
6.5 编写优化友好的代码
- 避免不必要的拷贝:使用引用传递大对象
- 信任编译器:简单直接地返回值,不要用复杂技巧
- 注意移动语义:C++11后,移动构造可以进一步优化
- 明确意图:当需要拷贝时显式表达,避免隐式拷贝
7. 对象生命周期管理
理解对象的构造和析构顺序对于资源管理至关重要。
7.1 存储类别与生命周期
不同存储类别的对象有不同的生命周期:
-
自动存储期(局部对象):
- 进入块时构造
- 离开块时析构(逆序)
-
静态存储期(static对象):
- 第一次使用时构造
- 程序结束时析构(逆序)
-
线程存储期(thread_local):
- 线程开始时构造
- 线程结束时析构
7.2 构造与析构顺序规则
-
局部对象:按照定义顺序构造,逆序析构(栈式)
cpp复制{ A a; // 先构造 B b; // 后构造 // 先析构b,再析构a } -
静态局部对象:第一次执行到定义时构造,程序结束时析构
cpp复制void func() { static A a; // 第一次调用func时构造 } -
全局对象:main前构造,main后析构(顺序可能不确定)
7.3 继承体系中的生命周期
派生类对象的构造和析构顺序:
-
构造顺序:
- 基类(按继承列表顺序)
- 成员对象(按声明顺序)
- 派生类自身
-
析构顺序:完全相反
cpp复制class Base {
// 基类
};
class Member {
// 成员类
};
class Derived : public Base {
Member m;
// 构造顺序:Base → Member → Derived
// 析构顺序:Derived → Member → Base
};
7.4 异常安全与资源管理
对象生命周期管理对异常安全的影响:
- RAII原则:资源获取即初始化
- 智能指针:自动管理动态内存生命周期
- 异常安全保证:
- 基本保证:不泄露资源
- 强保证:操作要么完全成功,要么完全回滚
- 不抛保证:承诺不抛出异常
7.5 最佳实践
- 遵循RAII:将资源管理封装在对象中
- 明确所有权:清楚每个资源的生命周期由谁管理
- 避免复杂生命周期:简化对象间依赖关系
- 注意静态对象:小心静态初始化顺序问题
- 使用现代C++特性:智能指针、移动语义等
8. 综合应用:设计模式中的类关系
通过设计模式案例展示类与对象的高级用法。
8.1 单例模式实现
cpp复制class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance; // 局部静态变量
return instance;
}
// 删除拷贝操作
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
private:
Singleton() {} // 私有构造函数
};
关键点:
- 私有构造函数防止外部创建
- 静态局部变量保证线程安全初始化
- 删除拷贝操作防止复制
8.2 工厂方法模式
cpp复制class Product {
public:
virtual ~Product() {}
virtual void use() = 0;
};
class Creator {
public:
virtual ~Creator() {}
virtual std::unique_ptr<Product> create() = 0;
void operation() {
auto p = create();
p->use();
}
};
class ConcreteCreator : public Creator {
public:
std::unique_ptr<Product> create() override {
return std::make_unique<ConcreteProduct>();
}
};
8.3 观察者模式
cpp复制class Observer {
public:
virtual ~Observer() {}
virtual void update() = 0;
};
class Subject {
public:
void attach(Observer* o) {
observers_.push_back(o);
}
void notify() {
for (auto o : observers_) {
o->update();
}
}
private:
std::vector<Observer*> observers_;
};
8.4 策略模式
cpp复制class Strategy {
public:
virtual ~Strategy() {}
virtual void execute() = 0;
};
class Context {
public:
void setStrategy(std::unique_ptr<Strategy> s) {
strategy_ = std::move(s);
}
void executeStrategy() {
if (strategy_) {
strategy_->execute();
}
}
private:
std::unique_ptr<Strategy> strategy_;
};
8.5 设计模式中的类关系技巧
- 组合优于继承:多用组合,少用继承
- 面向接口编程:依赖抽象而非具体实现
- 松耦合:减少类之间的直接依赖
- 单一职责:每个类只做一件事
- 开闭原则:对扩展开放,对修改关闭
9. 现代C++特性与类设计
C++11/14/17/20引入的新特性如何影响类设计。
9.1 移动语义与资源管理
cpp复制class ResourceHolder {
public:
ResourceHolder() : data_(new int[100]) {}
// 移动构造函数
ResourceHolder(ResourceHolder&& other) noexcept
: data_(other.data_) {
other.data_ = nullptr;
}
// 移动赋值运算符
ResourceHolder& operator=(ResourceHolder&& other) noexcept {
if (this != &other) {
delete[] data_;
data_ = other.data_;
other.data_ = nullptr;
}
return *this;
}
~ResourceHolder() { delete[] data_; }
private:
int* data_;
};
9.2 智能指针与所有权
cpp复制class Owner {
public:
Owner() : member_(std::make_shared<Member>()) {}
private:
std::shared_ptr<Member> member_;
};
class User {
public:
User(std::weak_ptr<Member> m) : member_(m) {}
void use() {
if (auto m = member_.lock()) {
// 使用m
}
}
private:
std::weak_ptr<Member> member_;
};
9.3 lambda表达式与函数对象
cpp复制class Processor {
public:
void process(const std::vector<int>& data,
std::function<void(int)> action) {
for (int x : data) {
action(x);
}
}
};
// 使用
Processor p;
p.process({1, 2, 3}, [](int x) {
std::cout << x * 2 << ' ';
});
9.4 constexpr与编译时计算
cpp复制class Circle {
public:
constexpr Circle(double r) : radius_(r) {}
constexpr double area() const {
return 3.141592653589793 * radius_ * radius_;
}
private:
double radius_;
};
// 编译时计算
constexpr Circle c(1.0);
constexpr double a = c.area();
9.5 三向比较运算符(C++20)
cpp复制class Comparable {
public:
int value;
auto operator<=>(const Comparable& other) const {
return value <=> other.value;
}
bool operator==(const Comparable& other) const {
return value == other.value;
}
};
10. 性能优化与陷阱规避
类设计中的性能考量和常见陷阱。
10.1 对象大小优化
-
空基类优化(EBCO):
cpp复制class Empty {}; class Derived : private Empty { int x; }; // sizeof(Derived) == sizeof(int) -
成员排列顺序:
cpp复制class BadLayout { char c; double d; int i; // 可能有填充字节 }; class BetterLayout { double d; int i; char c; // 更紧凑 };
10.2 虚函数开销
虚函数调用比普通函数调用多一次间接寻址:
cpp复制class Base {
public:
virtual void foo() {} // 虚函数表指针开销
};
class Derived : public Base {
void foo() override {}
};
优化建议:
- 避免不必要的虚函数
- 使用final标记不再被覆盖的虚函数
- 考虑CRTP模式替代多态
10.3 异常处理成本
异常处理会增加代码大小和运行时开销:
cpp复制class Resource {
public:
~Resource() noexcept(false) {} // 危险!析构函数抛异常
};
最佳实践:
- 析构函数声明为noexcept
- 避免在构造函数中抛异常导致资源泄漏
- 使用RAII管理资源
10.4 缓存友好设计
-
数据局部性:将一起访问的数据放在一起
cpp复制struct CacheFriendly { int key; int value; // 相关数据紧凑存储 }; -
避免虚假共享:
cpp复制struct alignas(64) AvoidFalseSharing { int thread1Data; // 填充到缓存行大小 char padding[64 - sizeof(int)]; int thread2Data; };
10.5 常见陷阱总结
-
对象切片:派生类对象赋值给基类对象导致信息丢失
cpp复制Base b = Derived(); // 切片,只保留Base部分 -
自赋值问题:赋值运算符未处理自身赋值
cpp复制class A { A& operator=(const A& other) { if (this != &other) { // 必须检查 // 赋值逻辑 } return *this; } }; -
异常不安全:操作中途抛异常导致状态不一致
-
循环引用:shared_ptr导致的循环引用内存泄漏
-
静态初始化顺序问题:不同编译单元的静态变量初始化顺序不确定
11. 实战案例分析
通过实际案例展示类与对象的高级应用。
11.1 线程安全队列实现
cpp复制template<typename T>
class ThreadSafeQueue {
public:
void push(T value) {
std::lock_guard<std::mutex> lock(mutex_);
queue_.push(std::move(value));
cond_.notify_one();
}
bool try_pop(T& value) {
std::lock_guard<std::mutex> lock(mutex_);
if (queue_.empty()) {
return false;
}
value = std::move(queue_.front());
queue_.pop();
return true;
}
void wait_and_pop(T& value) {
std::unique_lock<std::mutex> lock(mutex_);
cond_.wait(lock, [this] { return !queue_.empty(); });
value = std::move(queue_.front());
queue_.pop();
}
private:
mutable std::mutex mutex_;
std::queue<T> queue_;
std::condition_variable cond_;
};
11.2 对象池模式实现
cpp复制template<typename T>
class ObjectPool {
public:
using Pointer = std::unique_ptr<T, std::function<void(T*)>>;
ObjectPool() = default;
Pointer acquire() {
std::unique_lock<std::mutex> lock(mutex_);
if (pool_.empty()) {
return Pointer(new T(), [this](T* p) { release(p); });
}
auto ptr = std::move(pool_.back());
pool_.pop_back();
return Pointer(ptr.release(), [this](T* p) { release(p); });
}
private:
void release(T* p) {
std::unique_lock<std::mutex> lock(mutex_);
pool_.emplace_back(p);
}
std::vector<std::unique_ptr<T>> pool_;
std::mutex mutex_;
};
11.3 类型擦除设计
cpp复制class Any {
public:
template<typename T>
Any(T value) : concept_(std::make_unique<Model<T>>(std::move(value))) {}
Any(const Any& other) : concept_(other.concept_->clone()) {}
template<typename T>
T* cast() {
auto model = dynamic_cast<Model<T>*>(concept_.get());
return model ? &model->value_ : nullptr;
}
private:
struct Concept {
virtual ~Concept() = default;
virtual std::unique_ptr<Concept> clone() const = 0;
};
template<typename T>
struct Model : Concept {
Model(T value) : value_(std::move(value)) {}
std::unique_ptr<Concept> clone() const override {
return std::make_unique<Model>(value_);
}
T value_;
};
std::unique_ptr<Concept> concept_;
};
11.4 策略模式与模板结合
cpp复制template<typename Strategy>
class Context {
public:
void execute() {
Strategy::execute();
}
};
struct FastStrategy {
static void execute() {
// 快速执行策略
}
};
struct SafeStrategy {
static void execute() {
// 安全执行策略
}
};
// 使用
Context<FastStrategy> fastContext;
fastContext.execute();
11.5 编译时多态实现
cpp复制template<typename Impl>
class Interface {
public:
void execute() {
static_cast<Impl*>(this)->implementation();
}
// 静态多态
void run() {
static_cast<Impl*>(this)->run_impl();
}
};
class Implementation : public Interface<Implementation> {
public:
void implementation() {
// 具体实现
}
void run_impl() {
// 运行实现
}
};
12. C++类设计高级技巧
深入探讨类设计中的高级技术和模式。
12.1 奇异递归模板模式(CRTP)
cpp复制template<typename Derived>
class Base {
public:
void interface() {
static_cast<Derived*>(this)->implementation();
}
};
class Derived : public Base<Derived> {
public:
void implementation() {
// 具体实现
}
};
应用场景:
- 静态多态
- 方法链
- 对象计数
- 混合类
12.2 类型特征与SFINAE
cpp复制template<typename T>
class HasFoo {
private:
template<typename U>
static auto test(int) -> decltype(std::declval<U>().foo(), std::true_type{});
template<typename>
static std::false_type test(...);
public:
static constexpr bool value = decltype(test<T>(0))::value;
};
// 使用
static_assert(HasFoo<MyClass>::value, "MyClass需要提供foo()方法");
12.3 标签分发与特性基类
cpp复制struct FastPolicy {};
struct SafePolicy {};
template<typename Policy>
class Algorithm {
public:
void execute() {
execute_impl(Policy{});
}
private:
void execute_impl(FastPolicy) {
// 快速实现
}
void execute_impl(SafePolicy) {
// 安全实现
}
};
12.4 表达式模板优化
cpp复制template<typename Lhs, typename Rhs>
class VectorSum {
public:
VectorSum(const Lhs& l, const Rhs& r) : lhs(l), rhs(r) {}
double operator[](size_t i) const {
return lhs[i] + rhs[i];
}
private:
const Lhs& lhs;
const Rhs& rhs;
};
class Vector {
public:
template<typename E>
Vector& operator=(const E& expr) {
for (size_t i = 0; i < size; ++i) {
data[i] = expr[i];
}
return *this;
}
};
12.5 基于策略的设计
cpp复制template<
typename LockPolicy = StdLock,
typename AllocPolicy = DefaultAllocator
>
class ThreadSafeContainer : private LockPolicy, private AllocPolicy {
public:
// 使用策略提供的方法
void lock() { LockPolicy::lock(); }
void* allocate(size_t size) { return AllocPolicy::allocate(size); }
};
13. 跨平台与ABI兼容设计
考虑不同平台和编译器兼容性的类设计。
13.1 PImpl惯用法
cpp复制// 头文件
class Widget {
public:
Widget();
~Widget();
void doSomething();
private:
struct Impl;
std::unique_ptr<Impl> pImpl;
};
// 实现文件
struct Widget::Impl {
// 所有私有成员在这里
void privateMethod() {}
};
Widget::Widget() : pImpl(std::make_unique<Impl>()) {}
Widget::~Widget() = default;
void Widget::doSomething() {
pImpl->privateMethod();
}
优点:
- 隐藏实现细节
- 减少编译依赖
- 保持ABI稳定
- 方便跨平台实现
13.2 接口类设计
cpp复制class IInterface {
public:
virtual ~IInterface() = default;
virtual void method() = 0;
// 工厂方法
static std::unique_ptr<IInterface> create();
};
// 跨平台实现
class PlatformImplementation : public IInterface {
void method() override;
};
13.3 类型安全的跨边界传递
cpp复制class Handle {
public:
explicit Handle(int fd) : fd_(fd) {}
~Handle() { if (fd_ != -1) close(fd_); }
// 禁止拷贝
Handle(const Handle&) = delete;
Handle& operator=(const Handle&) = delete;
// 允许移动
Handle(Handle&& other) : fd_(other.fd_) {
other.fd_ = -1;
}
int get() const { return fd_; }
private:
int fd_;
};
13.4 版本化接口
cpp复制class IVersioned {
public:
virtual int version() const = 0;
virtual ~IVersioned() = default;
};
template<int Ver>
class VersionedImpl : public IVersioned {
int version() const override { return Ver; }
};
// 使用
auto obj = std::make_unique<VersionedImpl<2>>();
if (obj->version() >= 2) {
// 使用v2特性
}
13.5 二进制兼容性准则
- 不改变类布局:不添加/删除/重新排序成员变量
- 不改变虚表布局:不添加/删除虚函数
- 使用PImpl:将变化隔离在实现类中
- 版本控制:提供接口版本检测
- 谨慎使用STL:不同编译器STL实现可能不兼容
14. 测试与调试技巧
针对类与对象的测试和调试方法。
14.1 单元测试策略
-
测试接口而非实现:
cpp复制TEST(StackTest, PushIncreasesSize) { Stack s; s.push(42); ASSERT_EQ(1, s.size()); } -
模拟对象(Mock):
cpp复制class MockService : public Service { public: MOCK_METHOD(void, apiCall, (), (override)); }; -
私有方法测试:
- 使用友元测试类
- 或通过公有方法间接测试
14.2 调试技巧
-
对象布局检查:
cpp复制#define PRINT_OFFSET(Class, Member) \ std::cout << #Member << ": " << offsetof(Class, Member) << '\n' -
虚表检查:
bash复制
g++ -fdump-class-hierarchy -c myclass.cpp -
内存布局可视化:
cpp复制union { MyClass obj; unsigned char bytes[sizeof(MyClass)]; } u; // 检查u.bytes
14.3 性能分析
-
对象构造/析构开销:
cpp复制auto start = std::chrono::high_resolution_clock::now(); for (int i = 0; i < 1000000; ++i) { MyClass obj; } auto duration = std::chrono::high_resolution_clock::now() - start; -
缓存命中分析:
- 使用perf工具分析缓存命中率
- 检查对象大小和对齐
14.4 内存错误检测
-
地址消毒剂(ASAN):
bash复制
g++ -fsanitize=address -g myprogram.cpp -
未定义行为检测(UBSAN):
bash复制
g++ -fsanitize=undefined -g myprogram.cpp -
Valgrind工具集:
bash复制
valgrind --tool=memcheck ./myprogram
