访问器Visitor,使用典型的对象行为模式,它可以向不同的节点,施加用户自定义的操作,将这些操作整合到一个对象中,同时避免这些操作可能造成的数据结构本身的变化,从而实现了一种更加自由的编码方式。
每个数据元素节点都可以通过accept()方法调用访问器,访问器通过apply()方法获取传入的节点对象,并执行所需的节点操作。
osg::NodeVisitor只是访问器角色的抽象接口。我认为这里的接口不是C++标准定义的接口,查看了源码,在NodeVisitor中定义的是不同节点的apply虚函数,用于用户自定义的Visitor子类的重写,不是纯虚函数,这里书中叫做接口,应该是为了理解吧。
//——C++构造函数初始值列表————//——C++接口————
提供一个与其他系统交互的方法,为所有子类共同约定一个访问接口,提供给类外面使用的接口一般采用纯虚函数,如:virtual void work()=0;
接口实现:通过继承接口的子类实现,不同的子类可以实现不同的效果,这就是所谓的多态。
//——C++ 虚函数————
//基类将类型相关的函数与派生类不做改变直接继承的函数区别对待。对于某些函数,基类希望它的派生类各自定义适合自己的版本,此时//基类就将这些函数声明为虚函数virtual,即声明可以被覆盖。子类里的两个apply是重构父类方法的。
//C++规定,当一个成员函数被声明为虚函数后,其派生类中的同名函数都自动成为虚函数,因此,子类重新声明该虚函数时,可以加virtual关键字,也可以不加,但加上会更好,让程序层次更清晰。
#include "stdafx.h" #include<osg/Node> #include<osgDB/ReadFile> #include<iostream> class InfoVisitor :public osg::NodeVisitor{//Step1:继承并编写自己的访问器类 public: InfoVisitor() :osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), _indent(0){} virtual void apply(osg::Node& node)//Step2:重写各个节点类型对应的apply()函数,在其中实现自己所需功能。 { for (int i = 0; i < _indent; ++i) std::cout << " "; std::cout << "[" << _indent + 1 << "]" << node.libraryName() << "::" << node.className() << std::endl; _indent++; traverse(node); _indent--; } virtual void apply(osg::Geode& node) { for (int i = 0; i < _indent; ++i) std::cout << " "; std::cout << "[" << _indent + 1 << "]" << node.libraryName() << "::" << node.className() << std::endl; for (unsigned int n = 0; n < node.getNumDrawables(); ++n) { osg::Drawable*drawable = node.getDrawable(n); if (!drawable)continue; for (int i = 0; i <= _indent; ++i)std::cout << " "; std::cout << drawable->libraryName() << "::" << drawable->className() << std::endl; } _indent++; traverse(node); _indent--; } protected: int _indent; }; int _tmain(int argc, char** argv) { osg::ArgumentParser arugments(&argc, argv); osg::Node* root = osgDB::readNodeFiles(arugments); if (!root) root = osgDB::readNodeFile("axes.osgt"); InfoVisitor infoVisitor; if (root)root->accept(infoVisitor);//Step3:节点接受一个访问器,在accept()中节点执行访问器的apply() return 0; }