C++ 中派生类引用与基类引用的隐式转换
发表于|更新于
|浏览量:
C++ 中派生类引用与基类引用的隐式转换
转自:https://xiaopingqiu.github.io/,侵删
仅仅觉得都挺有用的,所以转发一下。仅仅是想做个备份,希望不要某天淹没在互联网的长河里
在读 OpenFOAM 代码过程中,有一类应用初看之下觉得很费解,比如 OpenFOAM-2.3.x 的 twoPhaseEulerFoam,createFields.H 有这么一段:
1 2 3 4 5
| phaseModel& phase1 = fluid.phase1(); phaseModel& phase2 = fluid.phase2();
volScalarField& alpha1 = phase1; volScalarField& alpha2 = phase2;
|
乍看之下,感觉有点奇怪:怎么能将 phaseModel 类的引用直接赋值给 volScalarField 类的引用呢?后来查看了一下 phaseModel 类的定义,发现原来 phaseModel 类是 volScalarField 类的派生,由此上面代码就好理解了,无非是将派生类引用赋值给基类引用而已。
但是,下面代码,虽然深究下去发现原理类似,但是乍看上去却更加费解:
1 2 3 4 5 6 7 8 9 10
| Foam::tmp<Foam::volVectorField> Foam::twoPhaseSystem::U() const { return phase1_*phase1_.U() + phase2_*phase2_.U(); } Foam::tmp<Foam::surfaceScalarField> Foam::twoPhaseSystem::calcPhi() const { return fvc::interpolate(phase1_)*phase1_.phi() + fvc::interpolate(phase2_)*phase2_.phi(); }
|
这里将 phase1_ 直接与 phase1_.U()(或者 phase1_.phi())相乘,怎么理解?从原理上讲,应该是 phase1_ 的体积分率与其速度的乘积。
仔细看一下 phaseModel 类的构造函数,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| Foam::phaseModel::phaseModel ( const twoPhaseSystem& fluid, const dictionary& phaseProperties, const word& phaseName ) : volScalarField ( IOobject ( IOobject::groupName("alpha", phaseName), // "alpha.particle" fluid.mesh().time().timeName(), fluid.mesh(), IOobject::READ_IF_PRESENT, IOobject::AUTO_WRITE ), fluid.mesh(), dimensionedScalar("alpha", dimless, 0) ), ...... ......
|
原来是这样,读取 “alpha.phaseName” 数据文件构建了一个 IOobject 对象,并用该对象对一个临时的 volScalarField 对象进行了初始化,然后用成员初始化列表,将该临时 volScalarField 对象对基类 volScalarField 进行初始化。
这样一来,对于上面的情形, phase1_ * phase1_.U() ,实际上是进行了一个隐式转换,先将 phase1_ 转换成基类 volScalarField 类型,由于上述 phaseModel 类的初始化设定,转换以后,phase1_ 其实就相当于用 alpha 初始化过的那个 volScalarField 类的对象了。所以, phase1_ * phase1_.U() 其实相当于 alpha.phaseName * phase1_.U() ,是两个 volScalarField 类对象之间的乘法运算。
在 twoPhaseEulerFoam的代码里,类似的用法还有很多,这里不能一一举例,当看到某处费解的时候,不妨想想是否是上面提到的情形。
为了便于理解这个原理,我这里写了一个简单的 c++ 测试小程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| #include<iostream> using namespace std;
class A { public: int a; A(int a); void print();
}; A::A(int a) { this->a = a; } void A::print() { cout<<"base class:"<<"a="<<a<<endl; }
class B : public A { public: int b; B(int a, int b); void print(); };
B::B(int a, int b):A(a) { this->b = b; } void B::print() { cout<<"Derived class:"<<"a="<<a<<",b="<<b<<endl; }
void print_test( A& obja) { obja.print(); }
int operator*(A& a, A& b) { return a.a * b.a; }
int main(int argc, char *argv[]) { int a=2,b=4;
A obj1(a); obj1.print();
B obj2(a+b,b); obj2.print();
A& obj3 = obj2; obj3.print(); cout<<"obj3.a="<<obj3.a<<endl; // cout<<"obj3.b="<<obj3.b<<endl; Error! class A has no member named 'b'.
print_test(obj1); print_test(obj2);
cout<<"obj1 * obj2 :"<< obj1 * obj2 << endl;
return 0; }
|
输出如下:
1 2 3 4 5 6 7
| base class:a=2 Derived class:a=6,b=4 base class:a=6 obj3.a=6 base class:a=2 base class:a=6 obj1 * obj2 :12
|
可以将上述程序中派生类对象赋值给基类的规律简单小结如下:
- 当将基类的引用指向派生类的对象时,用该引用只能调用派生类从基类继承而来的成员。像上面程序中,obj3.a 输出的是 对象 obj2 的成员 a,但是 obj3 无法调用成员b。
- 当一个函数需要的参数是基类的引用时,可以直接将派生类的对象传递给该函数,像上面的
print_test 函数和重载的 * 运算符那样。此事相当于作了一个隐式的将派生类对象赋值给基类引用。