關於C++中的override?

上圖中,子類中的self函數定義報錯。

重寫需要聲明一致,是為為了多態。

比如: 如果 我把 virtual BinaryExpr* newExpr();

改成: virtual int * newExpr();

報錯是:類型不一致, int * 不是派生自 Expr* 這很對。

那麼 最後一個, BinaryExpr 的確是派生自 Expr的,他報錯是類型不一致。那麼:下面這段代碼中的f函數就無法實現了,所以編譯器為什麼不允許類似於第三個函數那麼做?

#include&
using namespace std;
class A
{
public:
virtual A assign(){};
};

class B:public A
{
public:
int b;
virtual B assign()
{
B ba;
ba.b=1;
return ba;
}
};

class C:publicA
{
public:
double b;
virtual C assign()
{
C ca;
ca.b=1.1;
return ca;
}
};

void f(A * p)
{
*p = p-&>assign();
}

int main()
{
return 0;
}


如果容許這麼做,會導致Slicing。


首先請看這段代碼

#include &
using namespace std;
class Expr
{
public:
virtual char self()
{
return E;
}
};

class BinaryExpr: public Expr
{
public:
virtual char self()
{
return B;
}
};

int main(void)
{
Expr* e;
BinaryExpr* b=new BinaryExpr();
e=b;
char c = e-&>self();
cout&<&

這是在你原來的代碼上改出來的一個簡單的但是比較典型的虛函數的使用場景。

用代碼來講的話,虛函數的功效主要是,雖然別名e的類型是Expr,但是程序能自動的調用到BinaryExpr::self,而不是Expr::self。

但是虛函數有個限制,返回值的類型不能變。(詳細的後面再說)比如我把BinaryExpr::self的返回值類型改成了int或者是string,可是char c = e-&>self();的返回值在形式上只能是char啊,沒有辦法把各種各樣的類型塞到char里去。因為執行的時候已經棧上分配好了是一個位元組。

同樣地,把返回值類型從Expr改成BinaryExpr,也沒有辦法保證派生類的內存結構是一樣的。

當然你也發現了,破這個限定很簡單,把返回值類型改成指針或者引用就可以了,他們在同一個編譯器里的大小是一樣的。

最後有興趣的話,可以看下規範是怎麼說的。(ISO/IEC 14882:2003 10.3.5)

The return type of an overriding function shall be either
identical to the return type of the overridden function
or covariant with the classes of the functions.
If a function D::f overrides a function B::f,
the return types of the functions are covariant
if they satisfy the following criteria:
— both are pointers to classes or references to classes
— the class in the return type of B::f is the same class as
the class in the return type of D::f, or is an unambiguous
and accessible direct or indirect base class of the class
in the return type of D::f
— both pointers or references have the same cv-qualification
and the class type in the return type of D::f has the same
cv-qualification as or less cv-qualification than the class
type in the return type of B::f.
If the return type of D::f differs from the return type of
B::f, the class type in the return type of D::f shall
be complete at the point of declaration of D::f or shall
be the class type D. When the overriding function is called
as the final overrider of the overridden function,
its result is converted to the type returned by the
(statically chosen) overridden function (5.2.2).

[Example:
class B {};
class D : private B { friend class Derived; };
struct Base {
virtual void vf1();
virtual void vf2();
virtual void vf3();
virtual B* vf4();
virtual B* vf5();
void f();
};
struct No_good : public Base {
D* vf4();// error: B (base class of D) inaccessible
};
class A;
struct Derived : public Base {
void vf1();// virtual and overrides Base::vf1()
void vf2(int);// not virtual, hides Base::vf2()
char vf3();// error: invalid difference in return type only
D* vf4();// OK: returns pointer to derived class
A* vf5();// error: returns pointer to incomplete class
void f();
};
void g() {
Derived d;
Base* bp =d;// standard conversion:
// Derived* to Base*
bp-&>vf1();//callsDerived::vf1()
bp-&>vf2();//callsBase::vf2()
bp-&>f();// calls Base::f() (not virtual)
B* p = bp-&>vf4();// calls Derived::pf() and converts the
// result to B*
Derived* dp = d;
D* q = dp-&>vf4();// calls Derived::pf() and does not
// convert the result to B*
dp-&>vf2();// ill-formed: argument mismatch

—end example]


原答案沒說到點上,slicing是正解。

==========原答案==========

你這裡用的機制是重載,而不是虛函數覆蓋,虛函數覆蓋必須是完全相同的聲明,而僅返回值不同的函數重載是不允許的。

你的f函數想做什麼?


C++中對"子類型"這一要求的滿足是通過指針(包括智能指針)或者引用來體現的,而直接取其結構體的語義會導致slicing的產生


Expr* expr = new BinaryExpr();//OK

Expr expr BinaryExpr();//OK

Expr expr BinaryExpr();//Error


推薦閱讀:

C++中有哪些設計精良的部分(精華),還有哪些是不值得花費很多時間探究的知識點?
C++類對象內存大小的計算?
C++ 什麼時候不應該用虛函數?

TAG:面向對象編程 | 計算機科學 | C | 編譯原理 | 編譯 |