1

我遇到的问题是多态类:动物类有一个函数“virtual animal* get() = 0;” 在狗类中定义为"dog* get() { speak2(); return this; }"

我前段时间在某处读过,以这种方式更改返回类型是合法的,但它似乎不像我预期的那样工作:当调用 get 函数时,它会打印预期值,但是当我尝试将返回值分配给狗类指针我得到一个无效的转换错误,当我尝试调用speak2()函数时,它说它没有这样的成员。

我正在寻找的是能够按照“barn.front()->get()->speak2();”的方式调用一些东西。. 有没有任何方法可以在没有任何 dynamic_casts 或任何此类强制转换的情况下实现类似的东西?

我以合乎逻辑的方式命名了这些类,以便于阅读,并且还在以下代码中以注释的形式添加了一些注释:

#include <iostream>
#include <vector>
using namespace std;

class animal
{
public:
    virtual ~animal() {}
    virtual void speak1() = 0;
    virtual animal* get() = 0;
};

class dog : public animal
{
public:
    void speak1() { cout << "print-speak1!"; }
    void speak2() { cout << "print-speak2!"; }
    dog* get() { speak2(); return this; }
};

int main()
{
    vector<animal*> barn;

    barn.push_back(new dog());

    barn.front()->speak1();                // prints "print-speak1!"
    barn.front()->get();                   // prints "print-speak2!"

    barn.front()->get()->speak2();
    // error: 'class animal' has no member named 'speak2'
    // but then why does "barn.front()->get();" print "print-speak2!"?

    dog* dogptr = barn.front()->get();
    // error: invalid conversion from 'animal*' to 'dog*' [-fpermissive]

    dogptr->speak2();
    // for the sake of -Werror=unused-variable

    for(vector<animal*>::iterator i = barn.begin(); i != barn.end(); ++i)
    {
        delete *i;
    }
    barn.clear();

    return 0;
}
4

4 回答 4

1

对接口进行编程的重点是使用接口类作为访问点,并依赖实现的具体类型来符合接口。对您而言,这意味着您应该通过指向动物的指针访问向量中的实际动物。如果您将 speak2() 定义为动物类中的纯虚函数并使用 animal* 而不是 dog*,您的程序将运行。问题仍然存在,是否所有动物都具有 speak2() 能力,但这是设计的问题。请注意,我已将 using 指令放在类实现的下方,因为在库代码中使用命名空间指令不是一个好习惯。

这是更改的代码:

#include <iostream>
#include <vector>

class animal
{
public:
    virtual ~animal() {}
    virtual void speak1() = 0;
    virtual void speak2() = 0;
    virtual animal* get() = 0;
};

class dog : public animal
{
public:
    void speak1() { std::cout << "print-speak1!" << std::endl; }
    void speak2() { std::cout << "print-speak2!" << std::endl; }
    dog* get() { speak2(); return this; }
};

using namespace std;

int main()
{
    vector<animal*> barn;

    barn.push_back(new dog());

    barn.front()->speak1();                // prints "print-speak1!"
    barn.front()->get();                   // prints "print-speak2!"

    barn.front()->get()->speak2();
    // error: 'class animal' has no member named 'speak2'
    // but then why does "barn.front()->get();" print "print-speak2!"?

    animal* dogptr = barn.front()->get();
    // error: invalid conversion from 'animal*' to 'dog*' [-fpermissive]

    dogptr->speak2();
    // for the sake of -Werror=unused-variable

    for(vector<animal*>::iterator i = barn.begin(); i != barn.end(); ++i)
    {
        delete *i;
    }
    barn.clear();

    return 0;
}
于 2013-01-07T14:51:09.340 回答
1

您的代码在不同时间会发生两件事。首先,编译器看到barn.front()as的返回类型animal*。无论你做什么。speak2()在类型上调用成员animal*总是失败。

您的成员get()是一个虚拟函数,并且barn.front()->get();使用虚拟调度调用 with line。这意味着被调用的函数仅在运行时才知道,基于动物的真实(所谓的动态类型。这样你就可以修改get()每只动物的行为。

不同的返回值在get()这里无关紧要。编译器所做的类型检查(显然)是在编译时完成的,因此针对animal::get(). 仅当您直接调用静态类型的对象时,协变返回类型才有用dog

在您的情况下,一种骇人听闻的方式是将类型转换为dog,例如

static_cast<dog*>(barn.front())->speak2();

当然,如果 barn.front() 的实际类型不是狗,你会被烧毁。我的意思是未定义的行为——程序可能会崩溃、抛出异常或默默地继续损坏数据。

在我看来,更正确的方法是将不同的操作分成一个通用接口,例如:

class animal
{
public:
    virtual ~animal() {}
    virtual void makeSound() = 0;
};

class dog : public animal
{
public:
    void bark() { cout << "hoof"; }
    void makeSound() { bark(); }
};

class cat : public animal
{
public:
    void meow() { cout << "meow"; }
    void makeSound() { meow(); }
};

int main()
{
    vector<animal*> barn;
    barn.push_back(new dog());
    barn.push_back(new cat());
    barn.front()->makeSound();
    barn[1]->makeSound();
}
于 2013-01-07T15:15:56.660 回答
0

使用 时barn->front()->get(),您可以访问animal*指针。Speak2()没有为 定义animal,因此no member error.

于 2013-01-07T14:47:46.197 回答
0

给定

class animal
{
public:
    virtual animal * get () = 0;
};


class dog : animal
{
public:
    virtual dog * get () {...}
};

然后,

// Compiler sees call to "dog::get()".
dog * d = new dog();
d->get();


// Compiler sees call to "animal::get()", which may return
// an instance of any type derived from "animal".
animal * a = d;
d->get();
于 2013-01-07T15:32:04.827 回答