多态性概念

时间:2013-03-14 19:05:49

标签: c++

我在运行此程序时遇到“分段错误”。请区分以下两个程序

class xxx{
        public: virtual void f(){cout<<"f in xxx"<<endl;}    //virtual function
                virtual void g(){cout<<"g in xxx"<<endl;}    //virtual function
};
class yyy{           //yyy has no relation with xxx at this context
        public: virtual void f(){cout<<"f in yyy"<<endl;}  //virtual function but no relation with xxx class
                void g(){cout<<"g in yyy"<<endl;}
};
int main(int argc, char *argv[])
{
        xxx x1,*x;
        yyy y1;
        x=&x1;
        x->f();
        x->g();
        x=(xxx*) &y1;        //one class pointer containing another class object address
        x->f();
        x->g();
}

- 输出

f in xxx
g in xxx
f in yyy
Segmentation fault

但是根据多态性概念具有相同的问题

class xxx{
        public: virtual void f(){cout<<"f in xxx"<<endl;}     //virtual function
                virtual void g(){cout<<"g in xxx"<<endl;}     //virtual function
};
class yyy:public xxx{             //yyy is derived from xxx
        public: virtual void f(){cout<<"f in yyy"<<endl;}
                void g(){cout<<"g in yyy"<<endl;}
};
int main(int argc, char *argv[])
{
        xxx x1,*x;
        yyy y1;
        x=&x1;
        x->f();
        x->g();
        x=(xxx*) &y1;        //parent class pointer having derived class address
        x->f();
        x->g();
}

- 输出

f in xxx
g in xxx
f in yyy
g in yyy

2 个答案:

答案 0 :(得分:2)

在第一种情况下,强制转换会产生垃圾,因为你告诉编译器如果&y1指向xxx则不会。在第二种情况下,yyy xxx,因此将指向yyy的指针转换为指向xxx的指针是安全的。因为它一个。

如果您有指向卡车的指针,您可以将其视为指向车辆的指针,因为卡车是车辆。但是如果你有一个指向花的指针并将其视为指向车辆的指针,它本质上是一个垃圾指针,并且取消引用它是未定义的行为。

您应该使用C ++样式转换而不是C样式转换。如果你被迫表达预期的语义,那么抓住这种错误的可能性会更大。 (编写第一个代码的程序员是否认为它可以作为static_cast工作?或者他们认为它可以作为reinterpret_cast工作吗?额外的想法选择一个,或者在没有演员的情况下执行操作,可能已经避免了错误。)

答案 1 :(得分:0)

在你的第一个例子中:

    x=(xxx*) &y1;        //one class pointer containing another class object address
    x->f();
    x->g();

变量x实际上指向与类yyy无关的类xxxf()在两者中都被声明并且是唯一的虚函数的事实使它恰好工作。由于g()是类xxx中的虚函数,而不是yyy中的虚函数,因此编译器将生成代码来调用虚函数g(),其中包含一些垃圾。虚拟函数表中的f()之后发生的任何事情(又名vtable)[实际上虚函数的实现方式当然是一个实现细节,但是可以预期存在某种“表“存储在每个类的某个地方的函数”。

如果yyy被声明为这样,那么它会给你更有趣的结果:

class yyy{           //yyy has no relation with xxx at this context
        public: virtual void g(){cout<<"g in yyy"<<endl;}  //virtual function but no relation with xxx class
                void f(){cout<<"f in yyy"<<endl;}
};

它会告诉你,由于调用f()而导致“g yyy”,然后在调用g()时失败... ...假设虚函数工作的方式不是根据他们的名字调用它们,但是它们在类中的“顺序”[再次,确切地说它是如何工作的是一个实现细节,并且由于我们正在处理“未定义的行为”,编译器也被允许打印屏幕上闪烁的红色文字中的“答案是42”,或者在紧急呼叫时听起来像救护车 - 但这比我在典型的C ++编译器实现中所描述的更不可能发生。

在第二种情况下,由于virtual的{​​{1}}被继承到xxx,因此yyy中的函数g()也是虚拟的,因为“它的工作方式与你期望的一样“,因​​为你所做的正是你应该做的。

另请注意,有一些C ++样式转换可以帮助您避免出现这些错误 - 如果您做错了,它会在编译时发出错误消息(yyy),或者使用{{1在编译器无法在编译时确定类型的地方,它会给出static_cast作为结果,这是一个明显的指示,表明你弄错了。