美文网首页Java学习资料分享
C++模版,从精通到精神分裂!

C++模版,从精通到精神分裂!

作者: 编程说书酱 | 来源:发表于2019-12-13 16:46 被阅读0次

这是一篇写软件的文章,但是很硬,提前预警一下,女生不要看!

所有写C++的文章,如果没有源代码都是在耍流氓。闲话不说, May the source be with you!

// 第一段程序class Animal{public:    virtual void talk() = 0;};class Cat :public Animal{public:    virtual void talk(){cout<<"Miao\n";}};class Developer: public Animal{public:     virtual void talk(){cout<<"Hello world\n";}};void LetAnimalTalk(Animal* pa){    pa->talk();    }int main(){    Developer* pd = new Developer();    Cat* pc = new Cat();    LetAnimalTalk(pd);    LetAnimalTalk(pc);    vector<Animal*> va;    va.push_back(pd);    va.push_back(pc);    return 0;}

这是一个教科书般经典的例子。介绍C++的继承和多态。这里唯一需要重点强调的是:对函数LetAnimalTalk和vector va 来说,我们可以想象他们是客户。[face=黑体]通过继承把变化封装到基类的后面,这样使用基类接口的客户就不需要改动![/face]对客户来说,无论基类后面怎么变化,你都影响不到我。例如,如果现在有一个经理狗加入了项目团队,你的LetAnimalTalk函数是不需要任何改变的。

So far so good! 现在看看引入模版后,发生了什么?

//第二段程序template< typename T>class Animal{  public:    T id_;    Animal(T t){id_ = t;};    virtual void talk() = 0;};template<typename T>class Cat :public Animal<T>{  public:    Cat(T t):Animal<T>(t){};    virtual void talk(){cout<<"Miao, I am "<<Animal<T>::id_ <<"\n";}};template<typename T>class Developer: public Animal<T>{  public:     Developer(T t):Animal<T>(t){};    virtual void talk(){cout<<"Hello world, I am "<<Animal<T>::id_ <<"\n";}};template<typename T>void LetAnimalTalk(Animal<T>* pa){    pa->talk();    }int main(){    Developer<string>* pd = new Developer<string>("Yan");    Cat<string>* pc = new Cat<string>("Fluffy");    LetAnimalTalk(pd);    LetAnimalTalk(pc);    vector<Animal<string>*> va;    va.push_back(pd);    va.push_back(pc);    return 0;}

基本的应用场景是这样的。对于animal, 你可以用字符串来表示他的ID, 如果你想developer是不应该享有字符串名字的,那么你也可以用整型数来表示他的ID。上面整个的程序,如果你把main中换成下面的样子,除了猫会有点意见,其它一切都没有问题!

  Developer<int>* pd = new Developer<int>(9527);    Cat<int>* pc = new Cat<int>(1234);    LetAnimalTalk(pd);    LetAnimalTalk(pc);    vector<Animal<int>*> va;    va.push_back(pd);    va.push_back(pc);

上面模版继承的一个最明显的弊端是语法变得更加臃肿和复杂了。例如,你不可能在子类中直接引用基类的变量了。如果你引用他,必须使用Animal::id_这样的语法。背后的原因是当编译器编译Cat的时候,Animal是根本不存在的!具体的细节你可以看后面的参考文献1。

另外的一个问题就是虚函数的效率问题。这种多态是发生在程序运行的时候,主要通过虚函数表进行调用分发。所以有一定的效率损失。下面我们再看另外一个例子。由于猫不喜欢整型ID的名字,所以我们这里完全去掉这个feature,重点关注如何利用模版实现多态。

 //第三段程序 template< typename T> class Animal{   public:     void talk(){static_cast<T*>(this)->talkImplement();} }; class Cat :public Animal<Cat>{   public:     void talkImplement(){cout<<"Miao \n";}};class Developer: public Animal<Developer>{  public:    void talkImplement(){cout<<"Hello world \n";}};template<typename T>void LetAnimalTalk(Animal<T>& pa){    pa.talk();    }int main(){    Developer pd;    Cat pc;    LetAnimalTalk(pd);    LetAnimalTalk(pc);    vector<Animal<Cat>> va;    va.push_back(pc); // OK    // va.push_back(pd);    // compile error here    return 0;}

这段代码中,有几点注意一下:

1) 多态已经不需要用指针了,我们可以用引用来支持多态。

2)在函数LetAnimalTalk中,pa.talk()到底调用那一个 talkImplement是在编译的时候就决定了。这是一种静态多态技术。所以没有效率的损失。

3)但是函数LetAnimalTalk现在必须是模版函数,同时我们也失去了用vector同时保存Cat 和Developer的能力。这是效率提升带来的灵活性的损失!

4)这个是CRTP模式,更多介绍看参考文献2。翻译过来就是“好奇地不断追问自己!”这应该是一种精神分裂的明显的初期症状了。

目前为止,我们介绍了三个例子。还都遵循着一个基本的IS-A的逻辑关系。也就是说,Cat是一个Animal,Developer也是一个Animal。下面介绍三个IMPLEMENT-BY的逻辑关系。第一个例子完全没有使用继承。

//第四段程序template< typename T>class Animal{  private:    T t;  public:    void talk(){t.talkImplement();}};class SayMiao {  public:    void talkImplement(){cout<<"Miao \n";}};class SayHW{  public:    void talkImplement(){cout<<"Hello world \n";}};template<typename T>void LetAnimalTalk(Animal<T>& pa){    pa.talk();    }int main(){    Animal<SayMiao> am;    Animal<SayHW> ah;    LetAnimalTalk(am);    LetAnimalTalk(ah);    vector<Animal<SayMiao>> va;    va.push_back(am); // OK    //va.push_back(ah);    // compile error here    return 0;}

1) 这段程序中,通过模版参数,在编译的时候就把不同的talk行为的实现方式传递给Animal类。这个方法在STL中运用的相当广泛。具体的例子像STL中的map类

template<    class Key,    class T,    class Compare = std::less<Key>,    class Allocator = std::allocator<std::pair<const Key, T> >> class map;

其中, std::less就类似于我们上面的SayMiao。

2)由于导入的是某种行为,所有再叫做Cat就不合适了,所以这里把类的名字叫做SayMiao

为了实现IMPLEMENT-BY关系,我们也可以使用私有继承:

 //第五段程序 template< typename T> class Animal: private T{   public:     void talk(){T::talkImplement();} }; class SayMiao {   public:     void talkImplement(){cout<<"Miao \n";}};class SayHW{  public:    void talkImplement(){cout<<"Hello world \n";}};template<typename T>void LetAnimalTalk(Animal<T>& pa){    pa.talk();    }int main(){    Animal<SayMiao> am;    Animal<SayHW> ah;    LetAnimalTalk(am);    LetAnimalTalk(ah);    vector<Animal<SayMiao>> va;    va.push_back(am); // OK    //va.push_back(ah);    // compile error here    return 0;}

1) 有没有被

template

class Animal: private T{

这样的语法惊到!没关系,我们慢慢来。首先私有继承不是IS-A的关系。而是IMPLEMENT-BY的关系。关于什么时候使用私有继承,什么时候使用组合(composition)。请看参考文献3。

2)这种方式是Parameterised inheritance, 也是一种常见的设计模式,请看参考文献4

OK,最后的问题是,既然私有继承可以,共有继承行不行?在一个分裂的病人眼中,没啥是不行的!

//第六段程序template< typename T>class Animal: public T{};class SayMiao {  public:    void talk(){cout<<"Miao \n";}};class SayHW{  public:    void talk(){cout<<"Hello world \n";}};template<typename T>void LetAnimalTalk(Animal<T>& pa){    pa.talk();    }int main(){    Animal<SayMiao> am;    Animal<SayHW> ah;    LetAnimalTalk(am);    LetAnimalTalk(ah);    vector<Animal<SayMiao>> va;    va.push_back(am); // OK    //va.push_back(ah);    // compile error here    return 0;}

1) 这就是在Modern C++ design中提到的Policy-Based design。一个小提示是:现在在Animal中已经不需要talk这个函数了。

上面我一共给出了六个程序。到 https://www.onlinegdb.com/ 把这六段代码拷贝进去,根据自己的理解和问题修改一下。“纸上得来终觉浅,绝知此事要运行”。这其实是陆游给广大程序猿的一句忠告。如果你有足够的耐心,你可以慢慢地深入的体会,这里好玩的东西还挺多的。由于篇章关系(主要是再展开我也不会了!)我就不多说了。

以上这六段程序,分别代表着六种不同的语法方式,表达出两种最基本的设计模式 IS-A还是IMPLEMENT-BY。首先,没有什么优劣之分,在不同的场景下,各有优缺点。另外,C++的模版完全不同于传统的C++编程。他的语法和想表达的语义有非常明显的分裂趋势,非常容易把传统的C++程序猿也搞分裂了。正所谓范型是C++最大的坑,但是不跳此坑,不足以谈人生!

如果没有看懂就算了!你完全可以说:“这个人已经疯了!” 这个我在标题中已经承认了!

转载自:互联网

相关文章

  • C++模版从精通到精神分裂

    ​by - 赵岩 先做个广告置入,如果喜欢这篇文章,你可以到zhaoyan.website/blog去查看于此类似...

  • C++模版从精通到精神分裂

    点击关注异步图书,置顶公众号 每天与你分享IT好书 技术干货 职场知识 ​by - 赵岩 先做个广告置入,如果喜欢...

  • C++模版,从精通到精神分裂!

    这是一篇写软件的文章,但是很硬,提前预警一下,女生不要看! 所有写C++的文章,如果没有源代码都是在耍流氓。闲话不...

  • C++模版从精通到精神分裂

    这是一篇写软件的文章,但是很硬,提前预警一下,女生不要看! 交流群:728483370 所有写C++的文章,如果没...

  • 读书笔记17.06.03

    C++ STL:Listlist是C++标准模版库(STL,Standard Template Library)中...

  • macOS 下 C++ 模版的反汇编

    C++ 模版代码 先来一段简单的 C++ 模版的代码,代码如下: 代码的功能很简单的,分别传入 int 和 lon...

  • Xcode 添加 自定义 C++ 模版文件

    最近在研究 C++ , 发现xcode 自带的默认 C++ 文件模版不是忒好用,新建的 C++ 类文件 .hpp文...

  • C++ 模版

    模板(template)是为了支持泛型编程(Generic programming)而存在的,所谓泛型,也就是不依...

  • C++ 模版

    模版 模板是创建泛型类或函数的蓝图或公式。库容器,比如迭代器和算法,都是泛型编程的例子,它们都使用了模板的概念 每...

  • Android基于CMake进行OpenCV开发配置

    一、创建支持C++的android基础模版项目 新建项目,勾选【Include C++ support】,后续一直...

网友评论

    本文标题:C++模版,从精通到精神分裂!

    本文链接:https://www.haomeiwen.com/subject/otafnctx.html