Code of the Week #25: autoPtr<T>

#201912

程序段来源版本 foam-extend-3.1

接着上周的这一段程序 (取自 pisoFoam/createFields.H):

    singlePhaseTransportModel laminarTransport(U, phi);

    autoPtr<incompressible::turbulenceModel> turbulence
    (
        incompressible::turbulenceModel::New(U, phi, laminarTransport)
    );

我们这一次看第二段:

    autoPtr<incompressible::turbulenceModel> turbulence
    (
        incompressible::turbulenceModel::New(U, phi, laminarTransport)
    );

上面这段的直接意思是:类型为 incompressible::turbulenceModel 的智能指针,指向新构造的 incompressible::turbulenceModel 类的对象 turbulence。为什么要用智能指针呢?因为我们需要的是对象,而不仅仅是数据的 copy,因此,选择使用指针是最好的。再进一步, return 返回的是数据的 copy,想一想,如果你有 1E+6 个 vector 数据,这个量是很大的。

由于 C++ 在创建、使用、释放内存,对于大量数据的处理而言,如果要 copy 这一类数据,将要耗费很大的内存空间,因此,为了节约内存空间,使用 autoPtr 智能指针来转移数据 (或对象) 的 owner ,由 owner 来控制数据的处理 (delete)。autoPtr 指针所指的对象,只能由唯一指针所指,也就是只能有一对一的关系,而在 C++ 中可以多个指针同时指向一个对象。

autoPtr 是 OpenFOAM 对 C++ 智能指针概念的更进一步的应用。这里涉及到 C++ 的一个概念:NRVORVO (Return Value Optimization)。其中 RVO 是返回值优化,是这么一种优化机制:当函数需要返回一个对象的时候,如果自己创建一个临时对象用户返回,那么这个临时对象会消耗一个构造函数 (Constructor) 的调用、一个复制构造函数的调用 (Copy Constructor) 以及一个析构函数 (Destructor) 的调用的代价。而如果稍微做一点优化,就可以将成本降低到一个构造函数的代价,也就是将内容直接构造到左值中,中间不生成临时变量。

另外一个重要的概念是 RAII (Resource Acquisition Is Initialization),是 C++ 的一种管理资源、避免泄漏的用法,利用的就是 C++ 构造的对象最终会被 delete 的原则。RAII 的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,最后在对象析构的时候,释放构造时获取的资源。RAII 是 Bjarne Stroustrup 首先提出的。

智能指针拥有智能的析构函数和特殊的 copy 意义:值的 copy 实际上是 ownership 的转移,如果这个 tmp 对象由一个智能指针所指,则其析构函数不会 delete 这个对象。

autoPtr 的定义于:src/foam/memory/autoPtr/autoPtr.H,其中

template<class T>
class autoPtr
{
    // Public data

        //- Pointer to object
        mutable T* ptr_;
        ...
}

autoPtr 类只有一个成员,是指向某一个对象的指针。autoPtr 构造函数

template<class T>
inline Foam::autoPtr<T>::autoPtr(const autoPtr<T>& ap)
:
    ptr_(ap.ptr_)   // 将 ap 的指针赋值于 ptr_
{
    ap.ptr_ = 0;    // 将 ap 的指针置于空
}

将对象的所有权从 ap 转移到 *this 。因此,执行完构造函数之后,无法再从 ap 访问被管理对象。

autoPtr 重要的成员函数

template<class T>
inline T* Foam::autoPtr<T>::ptr()
{
    T* ptr = ptr_;
    ptr_ = 0;
    return ptr;
}

ptr()ptr_ 赋于 T 类型的 指针,再清空 ptr_ ,返回该对象的指针。因此,执行 ptr() 后, *this 不再拥有被管理对象的所有权。

参考文献:

  1. C++中的RAII机制
  2. RVO和NRVO
  3. 详解RVO与NRVO(区别于网上常见的RVO)
  4. understanding autoPtr
  5. OpenFOAM 中的 autoPtr
2 Likes