我遇到的问题是多态类:动物类有一个函数“virtual animal * get()= 0;”在dog类中被定义为 “dog * get(){speak2(); return this;}”。
我刚才在某处读过这样改变返回类型是合法的,但它看起来并没有像我预期的那样工作:当调用get函数时它打印出预期的值但是当我尝试分配时将值返回给dog类指针我得到了无效的转换错误,当我尝试调用 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;
}
答案 0 :(得分:1)
接口编程的重点是使用接口类作为接入点,并依赖于实现的具体类型来符合接口。对你而言,这意味着你应该通过指向动物的指针访问载体中的实际动物。如果将speak2()定义为动物类中的纯虚函数并使用动物*而不是dog *,则程序将运行。如果所有动物都具备speak2()能力,问题仍然存在,但这就是设计问题。请注意,我已将using指令放在类实现下面,因为在库代码中使用namespace指令不是一个好习惯。
以下是带有更改的代码:
#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;
}
答案 1 :(得分:1)
您的代码在不同时间发生了两件事。首先,编译器将barn.front()
的返回类型视为animal*
。无论你做什么。类型speak2()
上的呼叫成员animal*
始终失败。
您的成员get()
是一个虚拟函数,使用虚拟调度调用行barn.front()->get();
。这意味着被调用的函数仅在运行时已知基于什么是真实(所谓的动态)类型的动物。这样,您就可以修改get()
对每只动物的行为。
此处get()
的不同返回值无关紧要。编译器执行的类型检查(显然)是在编译时完成的,因此针对animal::get()
。协变返回类型仅在您直接调用静态类型dog
的对象时才有用。
在你的情况下,一种hackish方式是将类型转换为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();
}
答案 2 :(得分:0)
使用barn->front()->get()
时,您可以访问animal*
指针。 Speak2()
未定义animal
,因此no member error
。
答案 3 :(得分: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();