openFoam源码中涉及到的c++思想有很多,因为在计算流体力学中,域的创建尤为重要,我们可以在域中存储我们想要的物理变量如速度、压力等等。 首先:有一个宏观上的思维,我们用的大多数域对应的C++类是GeometricField,这个类里面包含了很多信息,但他的Base类其实是Field这个类,可以用下图来表示这个关系:
当然,一切要从代码看起: 首先看Field基类,找到Filed的.C和.H头文件开始分析: 该类都是在Foam这个大的命名空间底下,下面定义了很多模板类。
template<class Type>
class Field;
//- Pre-declare related SubField type
template<class Type>
class SubField;
template<class Type>
void writeEntry(Ostream& os, const Field<Type>&);
template<class Type>
Ostream& operator<<(Ostream&, const Field<Type>&);
template<class Type>
Ostream& operator<<(Ostream&, const tmp<Field<Type>>&);
// 类的前置声明
class dictionary;
这里用到了很多模板类的语法,首先声明了两个模板类的前置声明Field以及SubField,在这里要注意的是为什么要进行前置声明:
可以看到操作符重载的参数列表里用到了Field<Type>,这就必须进行前置声明,可能此时大家又有疑问,为什么操作符的重载也要进行前置声明呢,这是可以看到在Filed这个大的模板类里有这样一段友元函数的声明:
有这样一条规定:友元函数和运算符的前向声明:如果一个模板类里调用了友元函数(外面定义的方法可以使用该类里面的私有变量),而且这个友元函数里面的参数还用到了这个模板类,那么就得提前以模板的方式去声明这个类和函数。 这样大家就懂了头文件里前面这几行的声明的必要性。下面再看这个Filed类模板里面的一些难以理解的C++代码。
class Field:public tmp<Field<Type>>::refCount,public List<Type>
首先我们看到这里的Filed继承了List,相当于我们的Filed里面存储方式为一维数组的存储。 下面定义了一个copy函数:
const UList<Type>& copySelf(const UList<Type>& mapF,tmp<Field<Type>>& tmapFcpy) const;
这里看到前后都有一个const:前面的const好理解说明这个指针不能被改变,最后这const用于修饰该函数,表示在函数内不能改变其对应对象的成员变量的值。 接下来的typedef是用来声明两个类型的,第一个typename的作用是给编译器强调后面跟的是一个类型。
typedef typename pTraits<Type>::cmptType cmptType;
typedef SubField<Type> subField;
下来该类又使用了一个内联函数, 为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,只是建议内联不代表编译器真的会执行
inline static const Field<Type>& null()
{
return NullObjectRef<Field<Type>>();
}
下来定义了一些该类的构造器,其中比较有意思的是:
explicit Field(const label);
这里explict的解释我总结如下: C++中的explicit关键字只能用于修饰只有一个参数的类构造函数, 它的作用是表明该构造函数是显示的, 而非隐式的, 跟它相对应的 另一个关键字是implicit, 意思是隐藏的,类构造函数默认情况下即声明为 implicit(隐式).explicit声明之后,外面调用该构造函数必须显式声明, 因为构造器若只有一个参数并且默认是implicit,在外面调用的时候可以直接等于…, 编译器会自动给你补全,这样很容易产生歧义 例子: A(int size){ …构造函数里面的变量 } 外面调用可以直接A = 10;但是这个10并不代表size,不伦不类 接下来,该类还使用了一些复制构造函数,这都是我们平常开发不经常使用的操作:
Field(const Field<Type>&);
//- Copy constructor or re-use as specified.
Field(Field<Type>&, bool reuse);
//- Move constructor transferring the Field contents
Field(Field<Type>&&);
//- Copy constructor of tmp<Field>
Field(const tmp<Field<Type>>&);
复制构造函数的解释: 如果类的设计者不写复制构造函数, 编译器就会自动生成复制构造函数。 大多数情况下,其作用是实现从源对象到目标对象逐个字节的复制, 即使得目标对象的每个成员变量都变得和源对象相等。 在.C文件中,我们也可以看见一些有意思的写法,比如说模板构造函数,成员变量的直接初始化等等:
const char* const Foam::Field<Type>::typeName("Field");
Foam::Field<Type>::Field(): List<Type>(){}
看懂了这个文件,在相应的找到DimensionedField和GeometricField就可以看出他们之间的继承和每次继承完以后新添的一些功能创造出了一个GeometricField这样包含很多信息的域类。