有没有办法在函数中返回抽象类的不同变体而不会丢失切片信息?
同样地,我们说我有一个抽象类Dog,而狗有WildDog,DomesticDog,Puppy。
是否可以创建一个函数来接收其中一个函数并返回它们而不切片信息?
示例:
// Returns the type of dog you currently have
Dog getCurrentDog() {
return this.dog;
}
void setDog(Dog dog) {
this.dog = dog;
}
是否可以将WildDog或PuppyDog传递给setDog并保留相应类中的所有信息。举一个例子,PuppyDog有一个 drinkMilk()功能,其他狗不具备,但我仍然希望能够访问它。
如果可能的话,Java等价物会是什么?
我现在想到的解决方案是为Dog的每个实例提供一个 getDog(),它返回狗的特定实例,无论是Puppy还是野狗。这样,我就可以专门访问每个类中的代码。
答案 0 :(得分:4)
为避免切片,您需要将指针或引用传递给AudioUnitRender()
。请参阅{{3}}。
Dog
要使用std::shared_ptr<Dog> getCurrentDog()
{
return dog;
}
等中的函数,您需要在抽象PuppyDog
类或virtual
中声明它们Dog
。 dynamic_cast
通常表示设计不佳,但实际上,所有狗都可以喝牛奶,因此在dynamic_cast
课程中声明它们是有道理的:
Dog
现在您可以拥有以下内容:
class Dog
{
public:
virtual void drinkMilk(); // not pure virtual: all dogs can drink milk
};
class PuppyDog : public Dog
{
public:
// puppies drink less milk, and do it more messily
void drinkMilk() override;
};
有很多实例适用于: // getCurrentDog is a PuppyDog
std::shared_ptr<Dog> myDog = someOwner.getCurrentDog();
myDog->drinkMilk();
,play()
,rollOver()
以及所有其他狗可以做的事情;但是,假设你有一些并非所有狗都可以做到的功能,只有fetch()
可以。你仍然可以在你的基类中创建一个函数,并且可以将它声明为纯虚拟,或者你可以PuppyDog
到dynamic_cast
:
PuppyDog
正如@PaulMcKenzie指出的那样,它往往是一个设计缺陷,并且偏离了面向对象的编程(因为你最终得到了#34;如果它是// This if will only succeed if getCurrentDog is a PuppyDog (or something inherited PuppyDog) and if getCurrentDog != nullptr
if (std::shared_ptr<PuppyDog> myPuppy = std::dynamic_pointer_cast<PuppyDog>(someOwner.getCurrentDog()))
{
// do something only a puppy would:
myPuppy->beExtremelyCute();
}
,那么就这样做;如果它&#39; sa WildDog
,执行此操作&#34;等等)
答案 1 :(得分:0)
多态性通常更好地表示为包装类的实现细节。这为您提供了一个异构接口,例如可以存储在向量中并复制而不进行切片。
我在下面列出了一个工作示例。
请注意,各种类型的狗的实现实际上并不是多态的 - dog
,puppy
和wild_dog
。
多态性是作为一般狗类doggie
的实现细节引入的。
#include <iostream>
#include <typeinfo>
#include <memory>
#include <string>
#include <utility>
#include <type_traits>
#include <vector>
// default cases
template<class T>
void do_howl(const T&)
{
std::cout << "a " << T::dog_type() << " cannot howl" << std::endl;
}
template<class T>
void do_be_cute(const T&)
{
std::cout << "a " << T::dog_type() << " is not cute" << std::endl;
}
// now provide specialisations
struct dog
{
dog(std::string s) : name(std::move(s)) {}
std::string name;
static const char* dog_type() { return "normal dog"; }
};
void do_howl(const dog& d)
{
std::cout << "the dog called " << d.name << " is howling" << std::endl;
}
struct puppy
{
puppy(std::string s) : name(std::move(s)) {}
std::string name;
std::string cute_noise() const { return "yip yip!"; }
static const char* dog_type() { return "puppy"; }
};
void do_be_cute(const puppy& p)
{
std::cout << "aww! the cute little puppy called " << p.name << " is barking: " << p.cute_noise() << std::endl;
}
struct wild_dog
{
static const char* dog_type() { return "wild dog"; }
};
void do_howl(const wild_dog& d)
{
std::cout << "the nameless wild dog called is howling" << std::endl;
}
struct doggy {
struct concept {
virtual void be_cute() const = 0;
virtual void howl() const = 0;
};
template<class T>
struct model : concept
{
model(T&& t) : _t(std::move(t)) {}
model(const T& t) : _t(t) {}
void be_cute() const override
{
do_be_cute(_t);
}
void howl() const override
{
do_howl(_t);
}
T _t;
};
void howl() const {
_impl->howl();
}
void be_cute() const {
_impl->be_cute();
}
template<class T, std::enable_if_t<!std::is_base_of<doggy, T>::value>* = nullptr>
doggy(T&& t) : _impl(std::make_shared<model<T>>(std::forward<T>(t)))
{}
std::shared_ptr<concept> _impl;
};
int main()
{
std::vector<doggy> dogs = {
doggy(dog("rover")),
doggy(puppy("poochums")),
doggy(wild_dog())
};
for (const auto& d : dogs)
{
d.howl();
d.be_cute();
}
return 0;
}
预期产出:
the dog called rover is howling
a normal dog is not cute
a puppy cannot howl
aww! the cute little puppy called poochums is barking: yip yip!
the nameless wild dog called is howling
a wild dog is not cute
在这种特殊情况下,doggie
具有共享实现语义。如果您想要制作不同的副本,请将shared_ptr
替换为unique_ptr
,提供复制构造函数并向clone()
添加concept
方法(在model
)。