openfoam 智能指针探索

打印 上一主题 下一主题

主题 872|帖子 872|积分 2616

前言

今天看到一个程序,用到了智能指针,
  1. virtual tmp<volScalarField> rho() const;
复制代码
借此机会把有关智能指针的知识体系重新梳理一遍
智能指针autoPtr的由来:

首先要说明智能指针本质上是模板类,是对原有指针的改进,相比更安全,

of对autoPtr的描述如下:
An auto-pointer similar to the STL auto_ptr but with automatic casting
to a reference to the type and with pointer allocation checking on access.
of中的智能指针autoPtr很像原有的auto_ptr,但不是对原有的封装,而是重新写了一遍
再看std::auto_ptr
std::auto_ptr的定义大致如下:
  1. template <typename _Tp>
  2. class auto_ptr
  3. {
  4. private:
  5.     _Tp *_M_ptr;
  6. public:
  7.     explicit auto_ptr(_Tp *__p = 0) throw();
  8.     auto_ptr(auto_ptr &__a) throw();
  9.     auto_ptr &operator=(auto_ptr &__a) throw();
  10.     ~auto_ptr();
  11.     _Tp &operator*() const throw();
  12.     _Tp *operator->() const throw();
  13.     _Tp *get() const throw();
  14.     _Tp *release() throw();
  15.     void reset(_Tp *__p = 0) throw();
  16. };
复制代码
再看咱of中的autoPtr是何其相似,
  1. template<class T>
  2. class autoPtr
  3. {
  4.         mutable T* ptr_;
  5. public:
  6.     typedef T Type;
  7.         inline explicit autoPtr(T* = nullptr);
  8.         inline autoPtr(const autoPtr<T>&);
  9.         inline autoPtr(const autoPtr<T>&, const bool reuse);
  10.         inline ~autoPtr();
  11.             inline bool empty() const;
  12.             inline bool valid() const;
  13.             inline T* ptr();
  14.             inline void set(T*);
  15.             inline void reset(T* = nullptr);
  16.             inline void clear();
  17.             inline T& operator()();
  18.             inline const T& operator()() const;
  19.             inline T& operator*();
  20.             inline const T& operator*() const;
  21.             inline operator const T&() const;
  22.             inline T* operator->();
  23.             inline const T* operator->() const;
  24.             inline void operator=(T*);
  25.             inline void operator=(const autoPtr<T>&);
  26. };
复制代码
在autoPtr中,我们也能看到在autoPtr中加了很多unique_ptr的元素,比如说reset(),
那为什么要用智能指针呢,他的应用场景是哪些,下次我们自己写的时候要什么时候用
为什么要用智能指针:

举个例子,比如说我们要实现插值算法,用matlab写,这很简单
  1. result = function(input)
复制代码
现在我们学习C++了,知道了可以传指针或引用,可以这样写
  1. function(&result, input);
复制代码
相比之下of更倾向于使用matlab的书写方式
因为简单
不仅是看起来简单,写起来也简单,可以更直观的表达想法
对于没接触过C或C++的人来说,不必了解引用左值右值等一系列知识
在of中写动量方程,
  1. fvVectorMatrix UEqn
  2. (
  3.         fvm::ddt(rho, U)
  4.         + fvm::div(rhoPhi, U)
  5.         + turbulence->divDevRhoReff(rho, U)
  6. );
复制代码
首先这是个类fvVectorMatrix的构造函数,还是个拷贝构造
那这就需要括号内操作符重载以及函数返回类型都是fvVectorMatrix类
对于需要引入方程的人来说显式写法更直观更简单,如果写成function(&result, input)这样,一个两个还好,方程多了会非常乱
但是C++作为效率最高的语言,引用这个概念的提出肯定有他的道理
引用是什么,很多说是别名
实际上引用的本质是指针常量,如果换C语言的写法是这样的
  1. int* const rb = &a;
复制代码
matlab以简单易用著称,但用过matlab的人都知道matlab的效率极低,
本科时候当时不会向量化编程,参加数学建模比赛跑一个循环,跑了整整24小时,笔记本散热也不大行,后来送修主板了
为什么matlab效率低,很关键的一点是matlab一直都是复制拷贝
C/C++指针传地址效率就高很多,况且C++引用的本质就是指针,只不过是const修饰地址的指针
在简单易用和效率之间,matlab选择了前者,C++选择了后者
openfoam是一个非常强大的张量计算程序,既不能舍弃易用性抬高门槛,又不能反复使用复制拷贝降低效率,稀疏矩阵那么大拷贝来拷贝去算一个程序跑好几年成本太高
openfoam使用智能指针解决了这个问题,看起来不难读懂又能保证效率
这也就回答了为什么要使用智能指针
再回到我们刚刚说的动量方程拷贝构造上,首先在书写方法上依旧是matlab的显式书写方法,但实际上是C++的隐式移动拷贝
在哪里看到用指针了,可以打开fvm命名空间的内容
  1. namespace fvm
  2. {
  3. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
  4. template<class Type>
  5. tmp<fvMatrix<Type>>
  6. d2dt2
  7. (
  8.      const GeometricField<Type, fvPatchField, volMesh>& vf
  9. )
  10. {
  11.      return fv::d2dt2Scheme<Type>::New
  12.      (
  13.          vf.mesh(),
  14.          vf.mesh().d2dt2Scheme("d2dt2(" + vf.name() + ')')
  15.      ).ref().fvmD2dt2(vf);//这里返回的可是fvMatrix<Type>类型指针哦
  16. }
  17. template<class Type>
  18. tmp<fvMatrix<Type>>
  19. d2dt2
  20. (
  21.      const dimensionedScalar& rho,
  22.      const GeometricField<Type, fvPatchField, volMesh>& vf
  23. )
  24. {
  25.      return fv::d2dt2Scheme<Type>::New
  26.      (
  27.          vf.mesh(),
  28.          vf.mesh().d2dt2Scheme("d2dt2(" + rho.name() + ',' + vf.name() + ')')
  29.      ).ref().fvmD2dt2(rho, vf);
  30. }
  31. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
  32. } // End namespace fvm
复制代码
看到了嘛,随便一个fvm命名空间下文件,遍布tmp智能指针,
这里在指针赋值时就已经完成了类的初始化,但又因为只是指针,可以用显式的方法去写,只要保证返回类型相同即可
  1. fvm += fvc::surfaceIntegrate
  2. (
  3.         faceFlux*tinterpScheme_().correction(vf)
  4. );
复制代码
表面上是大型矩阵相加减,实际上是智能指针这个地址在代表执行
打个比方,这就像高启强要和赵立冬或孟德海商量一件事,赵和孟这个级别的不方便出面
出面的都是龚开疆或王秘书这样的人,又能传达指示又不消耗大量资源,好处就是双方都留有余地
王秘书见到高启强第一句话就是,“你知道我是代表谁来的吗”
实际在问,你知道我的哪个对象的智能指针吗
智能指针智能的点就在于不需要或者出问题的时候能自动销毁,打开相关析构函数
  1. template<class T>
  2. inline Foam::tmp<T>::~tmp()
  3. {
  4.     clear();
  5. }
复制代码
  1. template<class T>
  2. inline void Foam::tmp<T>::clear() const
  3. {
  4.     if (isTmp() && ptr_)
  5.     {
  6.         if (ptr_->unique())
  7.         {
  8.             delete ptr_;
  9.             ptr_ = 0;
  10.         }
  11.         else
  12.         {
  13.             ptr_->operator--();
  14.             ptr_ = 0;
  15.         }
  16.     }
  17. }
复制代码
tmp析构时对该智能指针进行了delete,autoPtr类似
记得狂飙里调查组一来最先销毁的也是龚开疆这个智能指针,,,
这样openfoam无需g++ -o优化也能有很好的运行效率
autoPtr与tmp的使用场合与区别

在openfoam中,autoPtr是强引用类型智能指针,tmp是弱引用类型智能指针
那我们在什么时候使用autoPtr以及tmp呢
autoPtr多使用在transport models ,boundry conditions,discretization schemes,turbulenceModel,interpolation schemes,gradient schemes或fvOptions这种动态多态中,更适合析构频次高的地方,智能指针autoPtr能够自动析构,因而被广泛使用
  1. autoPtr<incompressible::RASModel> turbulence
  2. (
  3.         incompressible::RASModel::New(U, phi, laminarTransport)
  4. );
复制代码
autoPtr一旦有所指向只能移动,不能复制,同名同类型只能指向一个对象
再说tmp,之前有博客说tmp类似shared_ptr,实际上tmp的自我介绍中并没有像autoPtr一样提及相关类auto_ptr,和shared_ptr也不是继承关系,但实现功能很接近
A class for managing temporary objects.
tmp的自我介绍中说是管理临时变量的类,这个介绍更像是我们日常做的副本,类似现在做的博客,害怕自己忘做份笔记,日后翻看,当然这个博客的建立首先是自己已经做好了理解,因而类似的,tmp的构造需要autoPtr在前面已经做好了指定,tmp配合进行副本引用
tmp的销毁和shared_ptr一致,具体可以见shared_ptr
一起探索openfoam也是相当有趣的一件事,非常欢迎私信讨论
指正的价值要比打赏更重要,下面是个人联系方式,希望能结交到志同道合的朋友


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

光之使者

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表