一些编程技术

大量编程资源
https://github.com/fffaraz/awesome-cpp


com 技术按我看来,他的思想是很有用的,实现dll二进制兼容,用 GUID,CLSID 等全局唯一的ID 标识组件的接口。
值得学习一番
code project 这个网站很多windows的技术和示例代码
https://www.codeproject.com/Articles/6726/COM-from-scratch-PART-ONE
https://www.codeproject.com/KB/COM/#Beginners

com的通俗解释

当然微软的官方文档也是必不可少的
https://docs.microsoft.com/en-us/windows/desktop/com/component-object-model--com--portal

https://channel9.msdn.com/Browse/AllContent
https://cppcon.org/
http://www.plauger.com/
www.gotw.ca
Herb Sutter 的博客和演讲,当然看这些视频演讲要了解演讲者的背景,说这话的动机,而不是盲目的跟随。 只有自己的技术水平够广够深,才能更好的judge别人的话


现代cpp 惯用法的更迭,当年的奇技淫巧
https://stackoverflow.com/questions/9299101/which-c-idioms-are-deprecated-in-c11

神奇的wiki网站
https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms


异常安全的要求(共同的基本要求:发生异常时,保证资源都及时释放,无资源泄露。)
1、无异常,必定成功,或者说该函数能正确处理所有可能的异常,不会让异常继续传播。
2、强异常保证,操作要么成功,要么回滚。此时对象的状态要么是成功后的,要么是操作发生前的(本质就是事务,所以额外代价可能很大)
3、基本的异常保证,发生异常时,对象都处于一个合法的状态(但对象的状态可能发生了改变,回不去操作发生之前的状态了)
https://docs.microsoft.com/zh-cn/cpp/cpp/how-to-design-for-exception-safety

cpp中典型的思路是 Resource Acquisition Is Initialization (RAII),用对象的生存期代表资源的生存期,只要析构和构造函数正确,即使异常发生也会调用析构函数释放资源。
智能指针就是重要工具。例如shared_ptr会共享资源所有权,资源的获取是用户决定的(何时new或者reset),而非对象构造的时候就获取资源。这类通过引用计数管理资源的方式可称为 Resource Release Is Destruction。

异常发生的难点

  • 异常可能在传参的时候构造参数时抛异常,或者在构造函数的初始值列表中构造成员的时候抛异常,也可能在函数体中抛异常。如果构造函数中出现异常,对象处于不完整的状态,不会调析构函数。此时构造函数中异常发生前new出来的部分资源没法在析构中delete。导致资源泄露。 因此要么都用智能指针保存new出来的资源,避免在析构函数中delete;要么资源类足够简单仅有一个资源要获取,只有这一处是抛异常的地方。

实现异常安全的方法:

  • 绝对不出异常的析构函数,这个是前提。
  • swap,移动构造,赋值等。
  • 保证资源类足够简单
  • function try block.
  • 处理无异常代码和有异常代码的边界

命名空间,实参类型查找,类作用域,using声明,重载 和 名字查找。

  • 名字查找先于类型检查。
  • 内层的函数/变量会隐藏外层的同名函数变量。例如每层花括号都是圈出一层作用域。基类是派生类的外层作用域,因此派生类新增了和基类同名不同参的函数,会隐藏掉基类的同名函数。
  • 实参相关的名字查找,调用一个函数的时候,除了会从本层作用域逐层向外查找,还会在实参的类所在的命名空间中查找。 例如 std::cout << 10; 即使操作符 << 是友元,但是并没有 using std::operator<<, 依然可以查找到,因为参数类型是ostream,在std之中。
  • using声明和普通声明一样,只在本作用域有效
  • 友元声明并不是函数声明,无法使得函数本身可见。
  • 局部作用域中的using 声明会将隐藏外层作用域的同名函数。会引入同作用域的同名函数作为重载函数,同时扩充候选函数集合。例如实现自定义的swap,以减少不必要复制。 void swap(type&a, type&b){ using std::swap; ... }需要对类成员逐个swap,这里的swap可以是成员自定义的swap或者 std::swap。此时众多的重载函数(swap通常需要一个非成员函数版本),候选函数集合就是当前作用域中所有可见的swap以及实参类型。

这里又扯到了为类定义一个特制的 swap操作的最佳实践。本来按我的想法,类成员函数定义一个swap( type& rhs ); 即可,调用的时候a.swap(b) 即可。这种显式调用对于各种具体类是没问题的。既不会调用到std::swap这个通用版本(可能造成不必要的深复制),也不会有名字冲突的可能。
但是缺点在于这个swap的被使用范围太小了。 容器内部用的是swap(a,b)的形式,因为类型type未必有swap成员函数,或者说避免要求类型参数提供swap成员,所以模板容器内不能用a.swap(b)的形式。
那么如果一个类仅仅提供了成员函数,那么在容器内只能调用std::swap, 而没法让类特制的swap生效。因此为了令特制的swap在容器里头生效,需要为类提供一个非成员函数 friend void swap( type&a, type&b){ ...交换类成员}; 或者 `void swap( type&a, type&b ) { a.swap(b); }
C++的名称查找规则:找到当前作用域可见的swap以及实参类型查找即(type所在的命名空间中的swap),这些swap都是重载函数的候选函数集合,从中找到精确匹配的最特化版本。

为了异常安全,swap也要保证不出异常并且标记noexcept,从而可用 copy&swap的手法保证异常安全。


获取数组长度

传统做法
#define countof( array ) ( sizeof( array )/sizeof( array[0] ) )

用宏的坏处是没有类型安全,不能阻止用户对指针的误操作。
用模板可以做到,核心思路是 数组类型的引用在模板类型推断时不会退化为指针。

template< typename T, size_t N>
constexpr size_t arrSize( T (&a)[N] ) noexcept
 { return N; }

参数采用引用的形式,数组的长度信息得以保留,N是根据类型自动推导出来的。感觉很神奇。还有两个细节就是 noexcept和 constexpr。 标记为noexcept可以向编译器提供更多信息做优化。 constexpr则使得函数可以在编译期使用,例如声明数组的长度之中。

在过去没有 constexpr,则需用难看的方法实现

template <typename T, size_t N>
char ( &ArraySizeHelper( T (&array)[N] ))[N];

#define countof( array ) (sizeof( _ArraySizeHelper( array ) ))

这里的 ArraySizeHelper是一个模板函数,接受一个长为N的数组的引用参数 T (&array)[N]返回一个长度为N的char数组的引用。 注意这个模板函数没有函数体的,仅仅是为了把这个模板函数的返回值的类型给 sizeof 操作符,从而得到N的值。


待了解的内容

effective modern c++ 关于类型推导的讨论,模板参数分为三大类

  • 指针、左值引用
  • 右值引用(传参的时候有引用折叠的情况)
  • 传值(值复制了,所以会略去实参的顶层const)
    其中数组的引用特别一点,不会退化为指针,同理的是函数的引用不会退化为函数指针。

成员函数的指针(静态成员函数,普通成员函数,虚函数三种)


模板的特化


异步的框架怎么做到(目测要事件,消息,发布订阅模式,消息队列?)多线程似乎不是必须的,例如js就是单线程的异步。


c++ 11 / c11 自带的同步设施,OpenMP的使用,SIMD的使用


跨语言边界调用的原理(参考swig等)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值