多态性概念

polymorphism concept

本文关键字:多态性      更新时间:2023-10-16

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

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

在第一种情况下,强制转换会产生垃圾,因为您告诉编译器在它没有指向xxx&y1表现得好像它指向了一个。在第二种情况下,yyyxxx,因此将指针投射到yyy指向xxx的指针是安全的,因为它是一个

如果您有指向卡车的

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

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

在第一个示例中:

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

变量x实际上指向与类xxx无关的类yyy。事实上,f() 在两者中都声明并且是唯一的虚拟函数,这使得它恰好起作用。由于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 in yyy",然后在调用g()时失败......由于虚函数的工作方式不是根据它们的名称来调用它们,而是根据它们在类中的"顺序"来调用它们[同样,确切的工作方式是一个实现细节,并且由于我们正在处理"未定义的行为",编译器也被允许在屏幕上以闪烁的红色文本打印"答案是42",或者听起来像紧急呼叫中的救护车 - 但这不太可能发生我刚刚在典型的C++编译器实现中描述的]。

在第二种情况下,由于xxxvirtual继承到yyy,函数g()yyy中也是虚拟的,因为"它按照您的预期工作",因为您正在做的事情正是您应该做的。

另请注意,有一些C++样式转换可以帮助您避免犯此类错误 - 如果您做错了,它将在编译时给出错误消息 ( static_cast (,或者如果您在编译器无法在编译时确定类型的地方使用 dynamic_cast,它会因此给出nullptr, 这是一个明显的指标,表明你弄错了。