SFINAE(Substitution Failure Is Not An Error )即匹配失败不是一个错误,这种技术是用在模板中,可用于判断类的某个属性是否存在,可把错误提前到编译期,这涉及到重载函数的匹配策略,具体看下面的代码分析。
template<typename T>
struct has_no_destroy
{
template <typename C> static char test(decltype(&C::no_destroy));
template <typename C> static int32_t test(...);
const static bool value = sizeof(test<T>(0)) == 1;
};
这段代码摘自muduo库的Singleton.h文件。
这里定义了一个has_no_destroy的struct,用于判断类中是否有no_destroy的属性;struct中声明了两个test重载函数,一个参数是decltype(&C::no_destroy),我们假设no_destory是typename C的成员变量,decltype是C++11关键字,用于推测表达式的类型,由于有&取地址符号,所以这里参数类型是一个指针,返回值是char类型;另一个参数是…,表示可以是任意的参数,也就是说无论test函数传递什么参数都会在这里得到匹配,它的返回值是4字节的int类型;最后还定义了一个bool值,注意看sizeof(test
if (!detail::has_no_destroy<T>::value)
{
……
}
我们都知道C++是一门面向对象的语言,它有四大范式:better C,OO,data abstraction,generic programing,其中很重要的一个就是OO,即面向对象,下面分别举例来说明面向对象编程和基于对象的编程。 假设我们需要设计一个绘图类Graphic,它的作用是画点,直线……面向对象写法如下:
/*
* OO, object-oriented implementation
* pure virtual function
*/
class Graphic
{
public:
virtual void drawPoint() = 0;
virtual void drawLine() = 0;
};
class SpecialGraphic : public Graphic
{
public:
void drawPoint() override {}
void drawLine() override {}
};
要实现绘图功能,通常做法是基类提供interface(纯虚函数实现),子类来实现行为,当然,不同的子类有不同的行为;这里,如果我们需要10种不同的绘图行为,那么需要10个子类来分别继承Graphic抽象父类来实现。下面看看基于对象写法:
/*
* object-based implementation
* std::function & std::bind
*/
class Graphic2
{
public:
typedef std::function<void()> DrawCallback;
Graphic2(){}
void drawPoint() { m_point(); }
void setPointCallback(DrawCallback callback) { m_point = callback; }
void drawLine() { m_line(); }
void setLineCallback(DrawCallback callback) { m_line = callback; }
private:
DrawCallback m_point;
DrawCallback m_line;
};
可以明显看到区别,基于对象写法已经不需要继承关系了:只需要利用function&bind组合来设置消息回调,大大降低了程序之间的耦合。 两种写法的用法区别也很大: void draw_point() {} void draw_line() {}
/// OO usage
Graphic *sg = new SpecialGraphic();
sg->drawPoint();
sg->drawLine();
/// object-based usage
Graphic2 g2;
g2.setPointCallback(bind(draw_point));
g2.setLineCallback(bind(draw_line));
g2.drawPoint();
g2.drawLine();
其实最重要的是,二进制兼容问题,二进制兼容是指在库文件升级时,不必重新编译使用了这个库的可执行文件或其他库文件,我们来分析下为什么面向对象不是二进制兼容的,而基于对象确是兼容的。 先说面向对象写法,假设现在需要升级库,在Graphic中添加drawCircle方法,由于interface都是虚函数,所以 新加虚函数会造成sizeof(Graphic)增大,而虚函数在内存中是由vtbl来维护的,访问虚函数是通过函数的offset来调用的,所以在可执行文件调用升级库的时候,会找不到新加的drawCircle方法,若要正常使用,可执行文件必须重新编译。 再来看基于对象写法,由于Graphic2中都是non-virtual函数,所以sizeof(Graphic2)大小并不会改变,完美实现二进制兼容。
搭建github+jekyll的博客目的是原博客太差劲了,之前我的博客托管于网易博客上,从今年(2018年)开始,发布于网站的博客文章经常会丢失,并且之前的文章也莫名其妙消失的迹象,鉴于此,寻找一个靠谱的博客托管网站实在是很有必要了我也有想过购买一个域名,租一台服务器,但对于我这种产量不高,且又喜欢记录的人来说,折腾建站太耗精力。有一次偶然发现,网上有些博客网址是github.io,进一步了解到github上可以搭建个人首页,于是乎,根据网友的贡献,自己fork了一套主题,读者现在看到的主题正是我fork自xudailong的repo,具体搭建步骤,请参考https://github.com/xudailong/xudailong.github.io,搭建过程根据自己电脑环境不同而问题也不同,总之发现问题要善于Google,始终相信:自己到现在才玩的东西,遇到的问题,别人肯定也已经遇到了。
博客主题也是fork自xudailong网友的repo,我并不会前端的修改,觉得他修改的主题还不错,便拿来用咯。
本博客主要记录我在学习C++和Python语言软件开发过程中的困难与感悟,文章的知识点会参考doc和优秀blog,但都是原创,我尽量保证知识点的正确性,代码尽量保证运行正确再上传。由于我也处于学习进步阶段,眼光和知识均有欠缺,如有不正确的地方还请阁下不吝赐教。
我在网易博客的地址是:http://mihooke.blog.163.com/ ,上面还保留写文章,是我学习过程中的一些笔记,以后就暂停更新了,新的文章会在此发布https://mihooke.github.io/
联系我:mihooke@hotmail.com