Appearance
C++ idioms
// TODO 持续补充记录。
一些 C++ 编程惯用技巧。
《More C++ Idioms》
RAII: Resource Acquistion is Initialization
RAII 的核心思想是"资源的获取就是初始化的过程"。 这一理念主张,资源的生命周期应该由对象的生命周期来管理。具体来说,就是对象通过构造函数获取资源,在析构函数里释放资源。
应用场景:
智能指针
句柄管理
互斥锁,xxx_guard (lock_guard, thread_guard)
and more...
注意事项:
禁止拷贝(Non-Copyable)
资源所有权的转移
对底层资源使用引用计数法
Pimpl: Pointer to Implementation
又叫编译防火墙,其核心思想是将类的实现细节封装到一个独立的类中,通过指针引用这个实现类,实现接口和实现的分离。
应用场景:
在 Qt 中大量使用:d-pointer,q-pointer,一般XXPrivate,XXData都是impl,比如QWidget 中有指向 QWidgetPrivate 的pimpl指针;
在编写动态库时,将类的实现放置于XXImpl中。
好处/优势:
信息隔离、二进制兼容性、实现细节的封装。
CRTP: Curious Recurring Template Pattern
奇异递归,一种实现静态多态的C++模板编程技巧,简单来说就是基类是类模板,派生类在继承该基类时,将自身作为模板参数传递给基类。
cpp
template<class DerivedT>
class Based {
public:
virtual ~Based() {}
void interface() {
static_cast<DerivedT*>(this)->implementation();
}
void implementation() {
std::cout << "Default implementation in Base \n";
}
};
class Derived: public Based<Derived> {
public:
void implementation() {
std::cout << "Custom implementation in Derived \n";
}
}; // ...
};应用场景:
std::enable_shared_from_this
多态拷贝构造
多态链式编程
好处:
- 静态多态发生在编译时,而动态多态发生在运行时,由此省去动态绑定、查询虚函数表带来得开销。
Erase-Remove
删除容器元素的同时缩小容器大小。std::remove 不会删除容器中的元素,只是将元素移动到容器的最后。这是因为std::remove使用的是forward iterator,而一般 forward iterator迭代器是无法删除容器中的元素的。
应用/实现:
std::remove 将“unused”的元素移动至容器末尾,并返回一个指向“unused“元素范围的开始;
容器的成员函数erase()可以删除容器特定范围内容的元素;
cpp
template<typename T>
inline void remove_erase(std::vector<T> &v, const T &item) {
v.erase(std::remove(v.begin(), v.end(), item), v.end());
}Non-Copyable/Non-Moveable
用于防止对象被复制,例如文件句柄、网络Socket等,使用该惯例能够避免如内存泄漏或多次释放的问题。
cpp
class NonCopyable{
protected:
NonCopyable () {}
~NonCopyable () {} /// Protected non-virtual destructor
NonCopyable (const NonCopyable &) = delete;
NonCopyable& operator= (const NonCopyable &) = delete;
};
class CantCopy : private NonCopyable
{};实现:
- 删除拷贝构造函数和拷贝复制运算符重载函数
Copy-and-Swap
用于实现拷贝赋值操作符,利用拷贝构造函数和交换操作,实现安全、高效且自我赋值安全的赋值运算符。
应用场景:
- 构造函数,拷贝构造函数,移动构造函数,拷贝赋值运算符重载函数,移动赋值运算符重载函数
实现:
拷贝构造函数(Copy Constructor):定义一个拷贝构造函数,用于创建对象的深拷贝
实现交换操作(Swap Operation):定义一个交换操作,用于交换两个对象的内容
赋值操作符(Assignment Operator):利用拷贝构造函数和交换操作,实现赋值操作符
Copy-on-Write
如果多个对象或变量共享相同的数据,那么它们可以共享同一份数据,而不是为每个对象创建独立的数据副本。
应用场景:
Qt 中的 QString
高并发读操作,写操作相对较少的场景
并发容器
实现原理:
浅拷贝 + 引用计数
当只是进行读操作时,就只进行浅拷贝,如果写操作时,再进行深拷贝,如果写操作时,再进行深拷贝。
好处:
内存效率提升
性能优化
减少非必要的内存拷贝
Type Erasure
// TODO 类型擦除
Mixin
// TODO