从OpenCV源码学习cpp_virtual

来源:互联网 发布:电信网络网上怎么测速 编辑:程序博客网 时间:2024/06/08 06:15

1. 先丢问题

看cpp源码,发现很多都有类继承树,就是先定义一个抽象基类,再派生,派生,派生。里面稍微复杂的地方,就是virtual关键字。比如这段代码:

class RandomTree{public:    virtual void split() = 0;    void train(){ split(); }};class AbstractSemanticSegmentationTree : public RandomTree{public:    virtual void split(){ cout << "spliting an node in AbstractSemanticSegmentationTree" << endl; }    void train(){ RandomTree::train(); }};class WeightedStdSSF : public AbstractSemanticSegmentationTree{public:};class StructClassSSF : public AbstractSemanticSegmentationTree{public:    virtual void split(){ cout << "spliting an node in StructClassSSF" << endl; }};

2. 分析

这段代码是一棵典型的类继承树,有抽象基类(RandomTree),有派生类。这段代码的用途是,描述随机森林,最后两个派生类分别对应一般随机森林方法和结构化随机森林方法。

由此可见,cpp里的多态,对描述算法里的“原算法”,“改进算法”是非常适合的。原算法和改进算法的相同部分,可以写在基类里面,而不同的部分可以分别在派生类里面重载不同的操作。太妙了!!!

2.1 简单例子

class A{public:    virtual void foo(){ cout << "A::foo()" << endl; }};class B : public A{public:    void foo(){ cout << "B::foo()" << endl; }};int main(int argc, char *argv[]){        A *a = new B;    a->foo();    return 0;}

这段代码是virtual的典型例子。如果有virtual修饰,a->foo()会调用B的foo(),如果没有,则调用A的foo()。这段代码表明,当使用A *a = new B这种方式实例化对象时,virtual会显现作用。

2.2 分析原例子

使用这个随机森林的实例代码如下:

int main(int argc, char *argv[]){    WeightedStdSSF *tree1 = new WeightedStdSSF;    tree1->train();    StructClassSSF *tree2 = new StructClassSSF;    tree2->train();    return 0;}

tree1的训练会使用AbstractSemanticSegmentationTree的分割函数,而tree2的训练会使用StructClassSSF的分割函数。

能有这种行为的原因是,RandomTree的split()函数是virtual,因此从RandomTree派生的子类和子子类,只要重载了split(),都带上了virtual的标签。这样一来,当需要调用这些split()时,它会搜索最符合要求的那个split()。比如这个例子里,当执行RandomTree::train(),两棵树的训练都需要执行这句话,但是并不是真正的同一句话。

tree1训练中执行的这句话,是在WeightedStdSSF实例化的对象的内存空间里,而tree2训练中执行的这句话,是在StructClassSSF实例化的对象的内存空间里,所以,其实是两句不同的话。

tree1训练,它会把所有split()按照“血缘关系”排序,选其中最亲的执行,tree1会选择AbstractSemanticSegmentationTree中的split()去执行。同理,tree2会选择StructClassSSF的split()去执行。

3. 更多

  1. virtual关键字是对派生类起作用,对当前类没有任何影响。
  2. 纯虚函数它也是virtual函数,也满足上面说的。只不过,在这基础之上,把本可以基类实现的代码,放在派生类中。这么做目前看来的主要动机是,提供一种类无法被实例化的机制,允许抽象类的存在。(抽象类的意义就在于,维护接口的统一,以及逻辑解耦和,使得接口和实现分开,实现部分的可以修改,而不影响已有的实现)
  3. 一旦某个成员函数被修饰了virtual(只有成员函数能够使用virtual),所有这个类派生的所有子类子子类中的重载函数,也都带上了virtual属性。所以,其实AbstractSemanticSegmentationTree和StructClassSSF中的virtual关键字是可以省略的,在功能上并不影响。而一般会注明,是为了时刻提醒程序员,这个成员函数是virtual,提高代码可读性。
0 0
原创粉丝点击