6

这是我的问题:我在 .h 文件中定义了一个虚拟方法,我想在从基类继承的类中调用它。可悲的是,派生类中的方法没有被调用。有没有更好的方法来实现我正在尝试做的事情?

#ifndef ofxBASE_SND_OBJ
#define ofxBASE_SND_OBJ

#include "ofConstants.h"

class ofxBaseSndObj {

public:

    virtual string getType(){}

    string key;

};

#endif

这是我的嗡嗡声课

#ifndef OFXSO_BUZZ
#define OFXSO_BUZZ

#include "ofxBaseSndObj.h"

class ofxSOBuzz : public ofxBaseSndObj
{
public:
    string getType();
};

#endif

ofxSOBuzz.cpp

string ofxSOBuzz::getType()
{
    string s = string("ofxSOBuzz");
    printf(" ********* returning string type %s", s.c_str()); // doesn't get called!
    return s;
}

然后在另一个类中,我尝试这样称呼它:

string ofxSndObj::createFilter(ofxBaseSndObj obj)
{
    string str = obj.getType();
    if(str.compare("ofxSOBuzz") == 0)
    {
        printf(" all is well ");
    }
}

在上面的方法中,我需要能够传入多种对象中的一种,这些对象都扩展了 ofxBaseSndObj 对象。任何建议或指示将不胜感激。谢谢!

4

6 回答 6

25

更改此行:

string ofxSndObj::createFilter(ofxBaseSndObj obj)

string ofxSndObj::createFilter(ofxBaseSndObj& obj)

您正在做的是按值传递(传递副本)。

这意味着您正在将对象复制到函数中。因为函数不知道你实际传递的是什么类型,所以它只传递函数声明中定义的类型,因此它会复制基类(这被称为切片问题)。

解决方案是通过引用传递。

如果您不希望函数修改对象(也许这就是您按值传递的原因,因此它无法更改原始对象),则传递一个 const 引用。

class ofxBaseSndObj
{
    public:
        virtual string getType()  const;
        // If the method does not change the object mark it const

        string key;

};

string ofxSndObj::createFilter(ofxBaseSndObj const& obj)
{
    // allowed to call this if getType() is a const
    string str = obj.getType();

    if(str.compare("ofxSOBuzz") == 0)
    {
        printf(" all is well ");
    }
}
于 2008-10-25T21:25:22.370 回答
10

您需要将实例作为指向对象的指针(或引用)传递给 createFilter。您是按值传递的,这会导致编译器将您用作参数的派生对象复制到基类的实例中。当它这样做时,您会失去它最初是派生类型的事实。

正如所写的那样,您的代码实际上不应编译,因为 ofxBaseSndObj::getType 的声明不返回任何内容。你的意思是这是一个抽象方法还是返回一个空字符串?

如果您将其设为抽象方法,那么编译器会抱怨尝试在您的 ofxSndObj::createFilter 方法中实例化抽象类。

于 2008-10-25T21:24:11.967 回答
2

这个问题在 C++中称为“切片” 。

于 2008-10-25T21:27:47.810 回答
2

将复制构造函数和 operator= 设为私有是防止此错误再次发生的有效方法。

例如:

class ofxBaseSndObj {
public:
    virtual string getType(){}
    string key;

private:
    ofxBaseSndObj(const ofxBaseSndObj& rhs);
    ofxBaseSndObj& operator=(const ofxBaseSndObj& rhs);
};

如果没有其他充分的理由,您应该使用 C++ 内置的 RTTI。然后,您可以使用 typeid 运算符。如果默认情况下未打开,请查看您的编译器文档以将其打开。

于 2008-10-25T21:40:20.077 回答
1

其他人已经解决了切片问题。然后你问好吧,让我说,我知道我需要做一些事情来确定基类型,但是有没有比进行枚举查找来确定继承对象的类型更优雅的事情?

查询和切换对象的类型是一个糟糕的设计,它错过了 OO 方法的要点。

代替

string ofxSndObj::createFilter(ofxBaseSndObj& obj)
{
    string str = obj.getType();
    if(str.compare("ofxSOBuzz") == 0)
    {
        // do ofxSOBuzz - specific thing
    }
    else if(str.compare("some other derived class") == 0)
    {
        // do stuff for other derived classes
    }
       // etc...
}

使有趣的行为成为虚函数:

class ofxBaseSndObj {

public:
    // get rid of getType()
    virtual void HelpCreateFilter() = 0;
};


string ofxSndObj::createFilter(ofxBaseSndObj& obj)
{
    // Let the derived class do it's own specialized work.
    // This function doesn't need to know what it is.
    obj.HelpCreateFilter();
    // rest of filter creation
}

为什么这个比原版好?因为ofxSndObj::createFilter如果xBaseSndObj的未来派生类被添加到系统中,不需要修改。您的版本需要为每个新的派生类扩展。如果不清楚,请尝试发布更多代码 - 我无法从您的代码或类名中看出这些函数应该做什么。

于 2008-10-25T22:39:10.690 回答
-1

您可以使用 dynamic_cast 或 type_id

于 2008-10-25T21:41:22.790 回答