这里列出了自己常用的一些c/c++小技巧, 有些会有不足, 可以简单探讨一下.
32位/64位等 分类
分类: 小技巧
同理可以用于其他位, 比如16位什么的. 由于不同位的平台指针的大小可能是不同的, 所以导致一些逻辑必须分别讨论.
很多时候我们可能不会在意是移动平台还是桌面平台, 但是肯定会在意指针的大小. c++的话可以使用模板特化方便地处理, 模板特化也是c非常难以模拟的特性之一.
最简单的, 比如我们想在32位平台是用单精度浮点而64位平台使用双精度浮点:
template<int T>
struct float_helper_t{
};
template<>
struct float_helper_t<4> {
using float_t = float;
};
template<>
struct float_helper_t<8> {
using float_t = double;
};
using mfloat_t = float_helper_t<sizeof(void*)>::float_t;
零代价pimpl
分类: 隐藏实现, 零代价
pimpl很好用, 但是不是零代价的. 不过对象大小是在编译器是固定(c++), 我们可以利用c++11的std::aligned_storage
创建一个零代价的pimpl. 同时针对不稳定API可以用static_assert
进行编译期断言.
例如WinAPI有一个SRWLOCK
, 表面上是一个指针. 虽然我们可以用指针重解释, 但是作为例子可以这么实现:
// 头文件
namespace detail {
template<size_t> struct rwlocker_impl_info {};
template<> struct rwlocker_impl_info<4> { enum { size = 4, align = 4 }; };
template<> struct rwlocker_impl_info<8> { enum { size = 8, align = 8 }; };
}
class CRWLocker {
enum { buf_size = detail::rwlocker_impl_info<sizeof(void*)>::size };
enum { buf_align = detail::rwlocker_impl_info<sizeof(void*)>::align };
protected:
std::aligned_storage<buf_size, buf_align>::type m_impl;
};
// 源文件
// 最好进行编译期断言
CRWLocker::CRWLocker() noexcept {
// WinAPI 的SRWLOCK
using ui_rwlocker_t = SRWLOCK;
static_assert(sizeof(ui_rwlocker_t) == buf_size, "must be same");
static_assert(alignof(ui_rwlocker_t) == buf_align, "must be same");
const auto locker = reinterpret_cast<ui_rwlocker_t*>(&m_impl);
::InitializeSRWLock(locker);
}
对于不稳定的API, static_assert
是非常重要的.
链表多态
分类: 实现技巧
基础数据结构中, 链表由于是指针的重要体现, 可以非常方便地处理多态:
------ ------ ------
node --> node --> node
------ ------ ------
data#1 data#2 data#3
------
------
------
例如比较常用的"工厂模式"创建的各个对象可以用链表串起来:
struct Node {
Node* prev;
Node* next;
};
struct Factory {
Factory();
Node head;
Node tail;
};
struct Obj1 : Node {
int a;
};
struct Obj2 : Node {
float a;
};
Factory::Factory() {
head.prev = nullptr;
head.next = &tail;
tail.prev = &head;
tail.next = nullptr;
}
每次添加节点可以在Factory::tail.prev
处做文章. 删除节点由于有头节点与尾节点的存在非常简单:
node.prev->next = node.next;
node.next->prev = node.prev;
多态的实现, 一般来说就是c++使用的虚函数. 不过注意的是虚表指针会占用一个指针的空间, 所以和节点的布局可以有两种:
A {
vtable*;
node;
};
B {
node;
vtable*;
};
一般选用A模式, B比较难实现. A模式又有一个面向对象常有的问题: 包含, 还是继承?
c++有一些自己不喜欢的东西, 这些东西都是属于, 对程序猿隐藏. A模式使用继承的话, static_cast
转换Node和继承类会隐含一个偏移判断, 这个隐藏没有问题. 问题是转换前会对指针进行判断, 如果是nullptr
的话, 转换后还是nullptr
, 这个很合理但是自己不喜欢, 添加了一个隐藏的分支.
如果大家对C/C++感兴趣的话,可以加一下我们的学习交流Q群:637 935 295,免费领取一套学习资料和视频课程哟~
所以自己可能会使用"包含"模式, 再使用offsetof
进行手动转换, 虽然offsetof
对于非标准布局是UB行为, 但是实际上不是offsetof
是UB, 而是非标准布局.