《Effective C++》 Reading Note 4

本文详细阐述了C++编程中的关键实践,包括模板元编程、特性类、新处理程序行为、新和删除替换时机等。同时介绍了标准库组件、Boost库及其在C++开发中的应用。涵盖了从基础库到高级库的广泛主题,旨在帮助开发者掌握C++的最佳实践和高效编程技巧。
条款44:将参数无关的代码抽离templates
factor parameter-independent code out of templates.

1.Templates生成多个classes和多个函数,所以
任何template代码都不该与某个造成膨胀的template参数产生相依关系。
2.因非类型模板参数(non-type template parameters)而造成的代码膨胀,
往往可以消除,做法是以函数参数或class成员变量替换template参数。
3.因类型参数(type parameters)而造成的代码膨胀,往往可降低,
做法是让带有完全相同二进制表述(binary representations)的具现类型
(instantiation type)共享实现码。

template<typename T, std::size_t n> // n就是非类型参数
class SquareMatrix{
public:
     void invert();
};


条款45:运用成员函数模板接受所有兼容类型
use member function templates to accept "all compatible types".

1.请使用member function templates(成员函数模板)
生成“可接受所有兼容类型”的函数。
2.如果你声明member templates用于“泛化copy构造”或“泛化ssignment操作”,
你还是需要声明正常的copy构造函数和copy assignment操作符。

template<typename T>
class SmartPtr{
public:
     template<typename U>
     SmartPtr(const SmartPtr<U>& other)
          : heldPtr(other.get()) {
          ...
     }
private:
     T* heldPtr;
};


条款46:需要类型转换时请为模板定义非成员函数
define non-member functions inside templates when type conversion are desired.

此条目相当复杂,所以最好查看原书。

当我们编写一个class template,而它所提供之“与此template相关的”函数支持“所有参数值之隐式类型转换”时,
请将那些函数定义为“class template内部的friend函数”。


条款47:请使用traits class表现类型信息
use traits classes for information about types.

traits广泛应用于标准程序库。
但是,平时使用很少,导致看得不深入,难懂。
最好,看原书的条款。此处不详述太多。

如何设计并实现一个traits class:
(1)确认若干你希望将来可取得的类型相关信息。如迭代器的种类。
(2)为该信息选择一个名称。如iterator_category。
(3)提供一个template和一组特化版本(如iterator_traits),内含你希望支持的类型相关信息。

// 对于5种迭代器种类,C++标准程序库分别提供专属的卷标结构(tag struct)
struct input_iterator_tag {};
struct output_iterator_tag {};
struct forward_iterator_tag : public input_iterator_tag {};
struct bidirectional_iterator_tag : public forward_iterator_tag {};
struct random_access_iterator_tag : public bidirectional_iterator_tag {};

template<...> // 略而未写的template参数
class deque{
public:
     class iterator{
     public:
          typedef random_access_iterator_tag iterator_category;
          ...
     };
};

template<...> // 略而未写的template参数
class list{
public:
     class iterator{
     public:
          typedef bidirectional_iterator_tag iterator_category;
          ...
     };
};

template<typename IterT>
struct iterator_traits{
     typedef typename IterT::iterator_category iterator_category;
     ...
}; // 它对用户自定义类型行得通,但对指针(另一种迭代器)行不通。
// 因为指针不可能嵌套typedef。

template<typename IterT>
struct iterator_traits<IterT*>{ // 利用偏特化解决,支持了指针迭代器
     typedef random_access_iterator_tag iterator_category;
     ...
}

// advance函数用于移动迭代器
// 以下代码是,利用重载技术,在编译期对类型执行取代if...else测试
template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, std::random_access_iterator_tag){
     iter += d;
}

template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, std::bidirectional_iterator_tag){...}

template<typename IterT, typename DistT>
void doAdvance(IterT& iter, DistT d, std::input_iterator_tag){...}


template<typename IterT, typename DistT>
void advance(IterT& iter, DistT d){
     doAdvance(iter, d, typename std::iterator_traits<IterT>::iterator_category();
     // 这里就不用if...else去测试用那种方式去移动迭代器了!
}

1.Traits classes使得“类型相关信息”在编译期可用。
它们以templates和“templates特化”完成实现。
2.整合重载技术(overloading)后,
traits classes有可能在编译期对类型执行if...else测试


条款48:认识template元编程
be aware of template metaprograming.

1.Template metaprograming(TMP,模板元编程)可将工作由运行期移往编译期,
因而得以实现早期错误侦测,和更高的执行效率!
2.TMP可被用来生成“基于政策选择组合”(based on combinations of policy choices)
的客户定制代码,也可用来避免生成对某些特殊类型并不合适的代码。

TMP已被证明是个“图灵完全”(turing-complete)机器,意思是它的威力大到足以计算任何事物。

TMP没有循环构件,循环效果由递归完成recursion。
但其递归非递归函数调用,而是“递归模板具现化”(recursive template instantiation)。
如下的阶乘例子(factorial):

template<unsigned n>
struct Factorial{
     enum { value = n * Factorial<n - 1>::value }; // enum hack
};
template<>
struct Factorial<0>{ // 模板特化
     enum { value = 1 };
};
只要你指涉Factorial<n>::value就可以得到n阶乘值。

为什么TMP值得学习?
(1)确保度量单位正确。使用它,就可以确保(在编译期)程序中所有度量单位的组合都正确,
不论计算多复杂。所以它能用于早期侦测。
(2)优化矩阵运算。
Matrix m1, m2, m3;
Matrix res = m1 * m2 * m3;
用高级与TMP相关的template技术,即expression templates,
就可能消除那些计算中临时产生的对象,并合并循环。
使其使用较少的内存,执行速度有大提升。
(3)可以生成客户定制之设计模式(custom design pattern)实现品。
设计模式如Strategy、Ovserver、Visitor等等都可以多种方式实现出来。
运用policy-based design之TMP-based技术,可能产生一些templates用来表述的独立设计选项,
可任意组合他们,导致模式实现品带着客户定制的行为。
例如智能指针。


条款49:了解new-handler的行为
understand the behavior of the new-handler.

STL容器所使用的heap内存是由容器所拥有的分配器对象(allocator objects)管理,
不是被new和delete直接管理。

当operator new抛出异常以反映一个未获满足的内存需求之前
它会先调用一个客户制定的错误处理函数,即new-handler

namespace std{
     typedef void (*new_handler)();
     new_handler set_new_handler(new_handler p) throw();
} // 其返回值也是一个指针,指向set_new_handler被调用前正在执行
// (但马上就要被替换)的那个new-handler函数。

当operator new无法满足内存申请时,会不断调用new-handler函数函数,直到找到足够内存。
设计良好的new-handler必须做以下事情:
(1)让更多内存可被使用。
(2)安装另一个new-handler。
如果目前这个new-handler无法取得更多可用内存,或许它直到另外哪个new-handler有此能力。
(3)卸载new-handler。
也就是将null指针传给set_new_handler。
一旦没有安装任何new-handler,operator new会在内存分配不成功时抛出异常。
(4)抛出bad_alloc(或派生自bad_alloc)的异常。
这样的异常不会被operator new捕捉,因此会被传播到内存索求处。
(5)不返回,通常调用abort或exit。

C++不支持class专属的new-handler,其实也不需要。
可令每个class提供自己的operator new和new-handler即可。

template<typename T>
class NewHandlerSupport{
public:
     static std::new_handler set_new_handler(std::new_handler p) throw();
     static void* operator new(std::size_t size) throw(std::bad_alloc);
     ...
private:
     static std::new_handler currentHandler;
};

template<typename T>
std::new_handler NewHandlerSupport<T>::set_new_handler(std::new_handler p) throw(){
     std::new_handler oldHandler = currentHandler;
     currentHandler = p;
     return oldHandler;
}

template<typename T>
void* NewHandlerSupport<T>::operator new(std::size_t size) : throw(std::bad_alloc){
     NewHandlerHolder h(std::set_new_handler(currentHandler));
     return ::operator new(size);
}
// 1.调用set_new_handler,告知Widget的错误处理函数。
// 2.调用global operator new,执行实际的内存分配。
// 如果失败,operator new会调用Widget专属的new-handler。
// 若最终还是无法分配足够内存,会抛出bad_alloc异常。
// 然而之后,还要恢复原本的global new-handler,
// 然后再传播该异常。为了将原来的handler安装回去,
// 使用了资源管理对象,实例即是以下的NewHandlerHolder,防止资源泄漏。
// 3.global operator new能够分配足够的内存,operator new会返回一个指针,指向分配所得。
// Widget的析构函数会管理global new-handler,
// 自动将Widget's operator new被调用之前的那个new-handler恢复回来~!

template<typename T>
std::new_handler NewHolderSupport<T>::currentHandler = 0;

class NewHandlerHolder{
public:
     explicit NewHandlerHolder(std::new_handler nh)
          : handler(nh){}
     ~newHandlerHolder(){
          std::set_new_handler(handler);
     }
private:
     std::new_handler handler;
     NewHandlerHolder(const NewHandlerHolder&);
     NewHandlerHolder& operator=(const NewHandlerHolder&);
};

1.set_new_handler允许客户指定一个函数,在内存分配无法获得满足时被调用。
2.Nothrow new是一个颇为局限的工具,因为它只适用于内存分配;
后继的构造函数调用还是可能抛出异常。

条款50:了解new和delete的合理替换时机
understand when it makes sense to replace new and delete.

为什么要替换编译器提供的operator new&delete呢?

(1)用来检测运用上的错误。
如,new的资源不小心delete掉时,导致内存泄露。
如,多次delete,导致了不确定行为。
这些都容易识别。但各式各样的编程错误导致
overruns(写入点在分配区块之后)或underruns(写入点在分配区块起点之前)。
自定义的operator new就可以分配额外空间防止特定byte pattern(即签名,signatures)
,检查分配区块在某生命时间点,是否发生了overruns或underruns。
(2)为了强化效能。
定制版性能可以优化超过缺省版本。
(3)为了收集使用上的统计数据。
(4)为了增加分配和归还的速度。
(5)为了降低缺省内存管理器带来的空间额外开销。
针对小型对象而开发的分配器(例如Boost的Pool程序库)本质上消除了这样的额外开销。
(6)为了弥补却生分配其中的非最佳齐位(suboptimal alignment)。
如doubles的访问在x86体系结构上的访问是最快速的。
(7)为了将相关对象成簇集中。
(8)为了获得非传统的行为。


条款51:编写new和delete时需固守常规
adhere to convention when writing new and delete.

详情最好自己重看原书。以下仅给出最后书中的简单概括:
1.operator new应该内含一个无穷循环,
并在其中尝试分配内存,如果它无法满足内存需求,
就应该调用new-handler。它也应该有能力处理0 bytes申请。
专属版本则还应该处理”比正确大小更大的(错误)申请“。
2.operator delete应该在收到null指针时不做任何事。
class专属版本则还应该处理”比正确大小更大的(错误)申请“


条款52:写了placement new也要写placement delete
write placement delete if you write placement new.

详情最好自己重看原书。以下仅给出最后书中的简单概括:
void* operator new(std::size_t) throw(std::bad_alloc); // normal new
void* operator new(std::size_t, void*) throw();          // placement new
                                                  // 从void*指针制定的位置开始分配内存
void* operator new(std::size_t, const std::nothrow_t&) throw(); // nothrow new

new和delete如果接受了额外参数,便称为placement的。
如果一个带额外参数的operator new没有”带相同额外参数“的对应版operator delete,
那么当new的内存分配动作需要取消并恢复旧观时就没有人员和operator delete会被调用。

1.当你写一个placement operator new,请确定也写出了对应的placement operator delete。
如果没有这样做,你的程序可能因为发生隐微而时断时续的内存泄露。
2.当你声明placement new和placement delete,请确定不要无意识(非故意)地掩盖了它们的正常版本。

条款53:不要轻忽编译器的警告
pay attention to compilere warnings.

1.严肃对待编译器发出的警告信息。努力在你的编译器的最高(最严苛)警告级别下
争取”无任何警告“的荣誉。
2.不要过度倚赖编译器的报警能力,因为不同的编译器对待事情的态度并不相同。
一旦移植到另一编译器上,你原本倚赖的警告信息有可能消失。

条款54:让自己熟悉包括TR1在内的标准程序库
 familiarize yourself with the standard library, including TR1.

STL:容器、迭代器、算法、函数对象、容器适配器、函数对象适配器。
iostream:自定缓冲功能。
国际化支持:wchar_t。
数值处理:复数模板complex、纯数值数组valarray。
异常阶层体系:exception、logic_error、runtime_error……
C89标准程序库:。。。

智能指针:。。。
tr1::function、tr1::bind
hash tables
正则表达式Regular expresstions
tuples变量组
tr1::array、tr1::mem_fn、tr1::reference_wrapper
随机数random number
数学特殊函数
c99兼容扩充

Type traits:用以提供类型(types)的编译器信息。
tr1::result_of:是个template,用来推导函数调用的返回类型。


条款55:让自己熟悉Boost
Familiarize yourself with Boost

Boost程序库对付的主题非常繁多,区分数十个类目,包括:
1.字符串与文本处理
2.容器
3.函数对象和高级编程(例如lambda)
4.泛型编程
5.模板元编程
6.数学和数值
7. 正确性与测试
8.数据结构
9.语言间的支持
10.内存
11.杂项

(1)Boost是一个社群,也是一个网站。
致力于免费、源码开放、同僚复审的C++程序库开发。
Boost在C++标准化过程中扮演深具影响力的角色。
(2)Boost提供许多TR1组件实现品,以及其它许多程序库。

内容概要:本文是一篇关于使用RandLANet模型对SensatUrban数据集进行点云语义分割的实战教程,系统介绍了从环境搭建、数据准备、模型训练与测试到精度评估的完整流程。文章详细说明了在Ubuntu系统下配置TensorFlow 2.2、CUDA及cuDNN等深度学习环境的方法,并指导用户下载和预处理SensatUrban数据集。随后,逐步讲解RandLANet代码的获取与运行方式,包括训练、测试命令的执行与参数含义,以及如何监控训练过程中的关键指标。最后,教程涵盖测试结果分析、向官方平台提交结果、解读评估报告及可视化效果等内容,并针对常见问题提供解决方案。; 适合人群:具备一定深度学习基础,熟悉Python编程和深度学习框架,从事计算机视觉或三维点云相关研究的学生、研究人员及工程师;适合希望动手实践点云语义分割项目的初学者与进阶者。; 使用场景及目标:①掌握RandLANet网络结构及其在点云语义分割任务中的应用;②学会完整部署一个点云分割项目,包括数据处理、模型训练、测试与性能评估;③为参与相关竞赛或科研项目提供技术支撑。; 阅读建议:建议读者结合提供的代码链接和密码访问完整资料,在本地或云端环境中边操作边学习,重点关注数据格式要求与训练参数设置,遇到问题时参考“常见问题与解决技巧”部分及时排查。
内容概要:本文详细介绍了三相异步电机SVPWM-DTC(空间矢量脉宽调制-直接转矩控制)的Simulink仿真实现方法,结合DTC响应快与SVPWM谐波小的优点,构建高性能电机控制系统。文章系统阐述了控制原理,包括定子磁链观测、转矩与磁链误差滞环比较、扇区判断及电压矢量选择,并通过SVPWM技术生成固定频率PWM信号,提升系统稳态性能。同时提供了完整的Simulink建模流程,涵盖电机本体、磁链观测器、误差比较、矢量选择、SVPWM调制、逆变器驱动等模块的搭建与参数设置,给出了仿真调试要点与预期结果,如电流正弦性、转矩响应快、磁链轨迹趋圆等,并提出了模型优化与扩展方向,如改进观测器、自适应滞环、弱磁控制和转速闭环等。; 适合人群:电气工程、自动化及相关专业本科生、研究生,从事电机控制算法开发的工程师,具备一定MATLAB/Simulink和电机控制理论基础的技术人员。; 使用场景及目标:①掌握SVPWM-DTC控制策略的核心原理与实现方式;②在Simulink中独立完成三相异步电机高性能控制系统的建模与仿真;③通过仿真验证控制算法有效性,为实际工程应用提供设计依据。; 阅读建议:学习过程中应结合文中提供的电机参数和模块配置逐步搭建模型,重点关注磁链观测、矢量选择表和SVPWM调制的实现细节,仿真时注意滞环宽度与开关频率的调试,建议配合MATLAB官方工具箱文档进行参数校准与结果分析。
已经博主授权,源码转载自 https://pan.quark.cn/s/bf1e0d5b9490 本文重点阐述了Vue2.0多Tab切换组件的封装实践,详细说明了通过封装Tab切换组件达成多Tab切换功能,从而满足日常应用需求。 知识点1:Vue2.0多Tab切换组件的封装* 借助封装Tab切换组件,达成多Tab切换功能* 支持tab切换、tab定位、tab自动化仿React多Tab实现知识点2:TabItems组件的应用* 在index.vue文件中应用TabItems组件,借助name属性设定tab的标题* 通过:isContTab属性来设定tab的内容* 能够采用子组件作为tab的内容知识点3:TabItems组件的样式* 借助index.less文件来设定TabItems组件的样式* 设定tab的标题样式、背景色彩、边框样式等* 使用animation达成tab的切换动画知识点4:Vue2.0多Tab切换组件的构建* 借助运用Vue2.0框架,达成多Tab切换组件的封装* 使用Vue2.0的组件化理念,达成TabItems组件的封装* 通过运用Vue2.0的指令和绑定机制,达成tab的切换功能知识点5:Vue2.0多Tab切换组件的优势* 达成多Tab切换功能,满足日常应用需求* 支持tab切换、tab定位、tab自动化仿React多Tab实现* 能够满足多样的业务需求,具备良好的扩展性知识点6:Vue2.0多Tab切换组件的应用场景* 能够应用于多样的业务场景,例如:管理系统、电商平台、社交媒体等* 能够满足不同的业务需求,例如:多Tab切换、数据展示、交互式操作等* 能够与其它Vue2.0组件结合运用,达成复杂的业务逻辑Vue2.0多Tab切换组件的封装实例提供了...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值