#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++ 的一个概念:NRVO 和 RVO (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
不再拥有被管理对象的所有权。