在C ++中使用Overriding感到困惑

时间:2010-10-31 21:04:34

标签: c++ override

我正在尝试使一个类继承自其他类并覆盖一些方法。类'标题'是:

class Objeto {  
public:  
    virtual bool interseca(const Rayo &rayo, float magnitud);  
    virtual bool breakNormal(const Punto &punto);  
    virtual Vector normal(const Punto &punto);  

    int idMaterial;  
};

class Esfera: public Objeto {
public:
    int idMaterial;

    virtual bool interseca(const Rayo &rayo, float magnitud);
    // etc
};

接下来在节目的其他地方(在Objeto和Esfera之外)我做:

// ObjectList is a Vector<Objeto>
Objeto o = esfera; /* Where esfera is a valid Esfera object */
ObjectList[0] = o;
ObjectList[0].interseca(rayo, magnitud);

我想要的是调用Esfera中的新版interseca。通过这种方式,我可以添加更多对象(立方体,三角形等),并将它们视为通用的“Objetos”。

但是,除了interseca的Esfera实施外,该计划还会调用Objeto::interseca

使用C ++执行此覆盖的正确方法是什么?这是覆盖的方式,我错过了什么,或者我是完全错的?任何提示或替代方法吗?

4 个答案:

答案 0 :(得分:7)

如果通过指针或引用访问类,则只能获得多态行为。除此之外,在将派生类型复制到非指针/非引用基类型时,您正在切片对象。

切片:

http://en.wikipedia.org/wiki/Object_slicing

鉴于这些定义:

#include<iostream>

class Objeto {  
public:  
    virtual bool interseca() {
        return false;
    }
};

class Esfera: public Objeto {
public:
    int idMaterial;

    virtual bool interseca() {
        return true;
    }
};

这不符合您的要求:

Esfera e;
Objeto o = e;
std::cout << o.interseca() << "\n";
  

但这会:

Esfera e;
Objeto& o = e;
std::cout << o.interseca() << "\n";
  

计划设计

将来可以用来避免这种情况的技术是使你的基类(纯)抽象。

你会在场景中实例化一个真实的Objeto,还是只是定义一个基本类型?如果您只是定义基本类型(我建议使用),那么您可以intersecabreakNormalnormal 纯虚拟。然后,编译器将捕获类似您在此处的问题。

class Objeto {  
public:  
    virtual bool interseca() = 0;
};

class Esfera: public Objeto {
public:
    int idMaterial;

    virtual bool interseca()
    {
        return true;
    }
};

// ...

然后,这没关系:

Esfera e;
Objeto& o = e;
std::cout << o.interseca() << "\n";
  

汇编成功

但是这会导致编译器出错 - 这是一件好事,因为它正在捕捉一个错误:

Esfera e;
Objeto o = e;
std::cout << o.interseca() << "\n";
  

错误:无法分配抽象类型'Objeto'的对象

答案 1 :(得分:3)

你的覆盖是正确的,但似乎你还没有完全掌握C ++的对象模型。这个

Objeto o = esfera;

不符合您的想法。此 会将Objeto esfera子对象的副本 创建为o。 (这称为“slicing”。)您想要的是使用参考引用 esfera

Objeto& o = esfera; // note the & 

或o指针

Objeto* o = &esfera;

如果有疑问,请始终 更喜欢参考

答案 2 :(得分:2)

您已成功覆盖该方法。问题在于存储对象的方式以及在C ++中实现多态行为的方式。

要获得多态行为,必须使用指针或引用引用对象。 C ++不支持与Java或C#相同的引用类型语义。如果您有一个像这样创建的变量

Objeto o = esfera;

变量o是基于对象Objeto创建的(静态和动态)类型esfera的新对象。这里发生的事情称为object slicing

要获得多态行为,您必须使用指针或引用。例如,我们假设变量objetoesfera属于ObjetoEsfera类型 分别

Objeto* op = &objeto;
op->interseca(rayo, magnitud); // virtual dispatch is used but since op points
                               // to an object of type Objeto Objeto::interseca
                               // gets called.
op = &esfera;
op->interseca(rayo, magnitud); // virtual dispatch is used and this time
                               // Esfera::interseca gets called.

您无法在一个vector中存储不同类型的对象。如果您创建vector<Objeto>并尝试在其中存储类型为Esfera的对象,则会发生切片,就像变量o的示例一样。

要拥有一个允许您获得多态行为的容器,您必须创建某种指针的vector。这意味着你将不得不解决对象所有权的问题,矢量元素将引用。最好的解决方案很可能是使用一些智能指针的向量。

答案 3 :(得分:0)

您在堆栈上创建了一个Objeto。此项目始终具有Objeto类型。对于多态性,您必须使用引用或指针。