英文:
Is it good to have a default implementation of all functions of all derived classes
问题
以下是翻译好的部分:
我正在尝试编写一个代码模型,其中有一个基类和两个不同的类继承自它,例如:动物类(Animal class)和狗类(Dog class)以及猫类(Cat class)继承自它。
Animal* a = new Dog();
playWith(a);
现在,playWith函数将以animal *
作为参数,并应该能够处理狗实例或猫实例,并调用可能是它们各自独有的各种函数,如狗的fetch或猫的climb。
狗和猫类都将具有一些相同的函数,这些函数将在动物类中声明和定义,但也有一些只属于其中一个的函数。
我已经通过在动物类中声明所有独有的函数,同时使用虚拟关键字和一个简单的错误实现来使代码工作。
然后,我在派生类中定义了实际的实现。
现在我的问题是,这是否是定义函数的好方法,如果不是,您能否建议一些替代方法?
更新:
我尝试做的最小示例如下:
class Animal {
public:
void walk(); // 所有类都可以调用此函数,并且它是从这里继承的
virtual void fetch(); // 它们有一个未实现的错误消息
virtual void climb();
}
class Dog : public Animal {
public:
void fetch();
}
class Cat : public Animal {
public:
void climb();
}
class Human { // 具有其他函数的复杂类
void playWith(Animal* a){ // 请注意,这可能是其他类的一部分,比如另一个类
...
}
}
int main(){
Animal a = new Dog();
Human h = new Human();
h->playWith(a);
}
当前,这可以使用派生类中正确的函数调用来工作。
更新2:
目前,我无法将它们声明为纯虚拟函数,因为猫类不知道如何fetch,而狗类不知道如何climb。如果我将它们声明为纯虚拟函数,那么它们就需要被定义。
英文:
I am trying to write a code model such that there is a base class and two different classes inherit from it, eg:- animal class with the dog and cat class inheriting
Animal* a = new Dog();
playWith(a);
now the playWith function will have animal *
as a parameter and should work with both the dog instance or cat instance and will call various functions that might be exclusive to them such as fetch for dog, or climb for cat
Both dog and cat classes will have some same functions which will be declared and defined in the animal class but there are some functions exclusive to only one
I have made the code work by declaring all the exclusive functions in the animal class also with a virtual keyword and an implementation of just throwing an error
And then I have defined the actual implementation inside the derived classes
Now my question is, Is this a good method to define functions, if not can you suggest some alternative
UPDATE:
A minimal example of what I am trying to do is
class Animal {
public:
void walk(); // all classes are able to call this function and it is inherited from here
virtual void fetch(); // they have an error message such as not implemented
virtual void climb();
}
class Dog : public Animal {
public
void fetch();
}
class Cat : public Animal {
public:
void climb();
}
class Human{ // complex class with other functions also
void playWith(Animal* a){ // Please note that this might be part of something else such as another class
...
}
}
int main(){
Animal a = new Dog();
Human h = new Human();
h->playWith(a);
}
This currently works with the correct function from derived class being called
UPDATE 2:
Currently I cannot declare them as pure virtual functions as the Cat Class does not know how to fetch and the Dog Class does not climb
and If I declare them to be pure virtual then they will need to defined
答案1
得分: 4
基类旨在捕捉共性。也就是说,只有在Animal
中应该声明walk
。从Animal
中,您可以进一步派生具有不同能力的其他种类的动物,如FetchableAnimal
和ClimableAnimal
,它们具有fetch
和climb
等不同的能力。
由于playWith
接受一个Animal
,因此预期playWith
适用于所有动物共享的能力。由于fetch
和climb
仅适用于某些动物,如果您打算在playWith(Animal*)
中调用它们,这将是一个设计上的矛盾。您可以考虑添加playWith(FetchableAnimal*)
和playWith(ClimableAnimal*)
来处理这种情况。
总之,基类应该捕捉特征的交集,而不是并集。
英文:
Base class is meant to capture commonalities. That said, only walk
should be declared in Animal
. From Animal
, you may further derive other kinds of animals with different capabilities like fetch
and climb
like FetchableAnimal
and ClimableAnimal
.
Since playWith
takes an Animal
, it's expected that playWith
works with capabilities that all animals share. Since fetch
and climb
are exclusive to certain animals, it's a design contradiction if you intend to invoke them in playWith(Animal*)
. You may consider adding playWith(FetchableAnimal*)
and playWith(ClimableAnimal*)
for such cases.
The bottom line is that base class is supposed to capture feature intersection, not union.
答案2
得分: 2
你正在寻找的替代方法是一个纯虚函数。
virtual void playWith(animal *) = 0;
编译器会强制子类创建一个实现,这比运行时错误要好得多。现在,这其中还有一些看起来风格上不正确的地方。如果playWith
是Animal
类的一个方法(建议类名以大写字母开头),那么就不会有参数。如果它应该有一个参数,并且使用this
与传入的动物一起玩耍,那么我会通过引用传递:playWith(Animal& a)
。
还要避免使用裸指针。其他的动物能否在堆栈上创建?如果不能,你应该考虑使用std::unique_ptr
或者std::shared_ptr
。
英文:
The alternative you are looking for is a pure virtual function.
virtual void playWith(animal *) = 0;
The compiler will force subclasses to create an implementation, much better than a runtime error. Now, there are other parts of this that don't look stylistically correct. If playWith
is a method of the Animal
class (suggest classes start with a capital letter), then there would be no parameter. If it is supposed to have a parameter, with this
playing with the passed in animal, then I would pass by reference: playWith(Animal& a)
.
Raw pointer use is also discouraged. Can the other animal be created on the stack? If not, you should consider std::unique_ptr
or std::shared_ptr
.
答案3
得分: 1
这是我在 Stack Overflow 上的第一次输入。整理了你说的内容后,请尝试我刚写的程序,看它是否符合你的想法。
#include <iostream>
class Animal {
public:
virtual void played_with() = 0;
};
class Dog : public Animal {
public:
void played_with() { fetch(); }
private:
void fetch() { std::cout << "我在接球。\n"; }
};
class Cat : public Animal {
public:
void played_with() { climb(); }
private:
void climb() { std::cout << "我在爬。\n"; }
};
class Human {
public:
// 在这里,引用比指针更方便。
void play_with(Animal& a) { a.played_with(); }
};
int main()
{
Dog d;
Cat c;
Human h;
h.play_with(d);
h.play_with(c);
return 0;
}
我使用的是 Visual Studio 2022。输出结果是:
我在接球。
我在爬。
英文:
It's my first typing in stack overflow. After arranging things you are talking, please try the program below I just wrote to see if it can meet what you are thinking.
#include <iostream>
class Animal {
public:
virtual void played_with() = 0;
};
class Dog : public Animal {
public:
void played_with() { fetch(); }
private:
void fetch() { std::cout << "I am fetching.\n"; }
};
class Cat : public Animal {
public:
void played_with() { climb(); }
private:
void climb() { std::cout << "I am climbing.\n"; }
};
class Human {
public:
// Reference is more convenient than pointer here.
void play_with(Animal& a) { a.played_with(); }
};
int main()
{
Dog d;
Cat c;
Human h;
h.play_with(d);
h.play_with(c);
return 0;
}
I was using Visual Studio 2022. The output is:
I am fetching.
I am climbing.
答案4
得分: -1
C++中多态的一个方面是将函数声明为virtual
,并让每个派生类根据需要进行override
。
一旦你实例化了一个Dog对象,你就调用它的重写方法,从而防止跨调用其他派生类的方法。
英文:
One aspect of polymorphism in C++ is to declare your functions as virtual
and each of your derived classes override
them as of their need.
Once you instantiate a Dog object, you call its overriden method, as such preventing cross calling other derived classes methods.
答案5
得分: -4
请注意,由于代码部分不需要翻译,我将只返回翻译好的部分。以下是您要翻译的内容:
不要将独占功能作为基本 animal
类中的虚拟函数。这是一个相当糟糕的设计。
而是创建新的全局函数,接受 dog
或 cat
实例,并使用 downcasting 将 animal*
指针强制转换为正确的类型:
dogOnlyFunction(static_cast<dog*>(a));
虽然设计,甚至需求及其分析可能从一开始就存在缺陷,但您可以使用 动态 下转型来解决它,在运行时进行检查:
void playWith(animal* a)
{
a->walk(); // 共同的功能
if (auto d = dynamic_cast<dog*>(a); d != nullptr)
{
d->fetch(); // 狗特有的功能
}
else if (auto c = dynamic_cast<cat*>(a); c != nullptr)
{
c->climb(); // 猫特有的功能
}
}
正如从这个示例中可以看出的那样,当添加更多类型时,这种方法不会很好扩展。因此,更好的解决方案可能是创建一个虚拟的 成员 函数 playWidth
,在子类中进行重写,并调用它们的特定函数:
struct animal
{
virtual void playWidth() = 0;
virtual void walk() = 0;
};
struct dog : public animal
{
void playWidth() override
{
walk(); // 调用共同的功能
fetch(); // 调用狗特有的功能
}
void walk() override
{
// TODO: 行走
}
void fetch()
{
// TODO: 取东西
}
};
struct cat : public animal
{
void playWidth() override
{
walk(); // 调用共同的功能
climb(); // 调用猫特有的功能
}
void walk() override
{
// TODO: 行走
}
void climb()
{
// TODO: 攀爬
}
};
在上面的示例中,我将共同的 walk
函数保留为基类中的纯虚函数,以便所有子类都需要重写它。如果并非所有子类都应该有 walk
函数,那么它可以像 fetch
和 climb
一样变成类特定的函数。
现在,不再需要全局的 void playWith(Animal*)
函数,只需调用 a->playWidth();
。
英文:
Don't put the exclusive functions as virtual functions in the base animal
class. That's a rather bad design.
Instead create new global functions that take either a dog
or a cat
instance, and use downcasting to cast the animal*
pointer to the correct type:
dogOnlyFunction(static_cast<dog*>(a));
While the design, maybe even the requirements and their analysis, seems flawed to begin with, you could solve it with dynamic downcasting, which is checked at runtime:
void playWith(animal* a)
{
a->walk(); // Common function
if (auto d = dynamic_cast<dog*>(a); d != nullptr)
{
d->fetch(); // Dog specific function
}
else if (auto c = dynamic_cast<cat*>(a); c != nullptr)
{
c->climb(); // Cat specific function
}
}
As can be noted from this example, this doesn't scale well when more types are added. So a perhaps better solution is to make a virtual member function playWidth
which is overridden in the child classes and calls their specific functions:
struct animal
{
virtual void playWidth() = 0;
virtual void walk() = 0;
};
struct dog : public animal
{
void playWidth() override
{
walk(); // Call the common function
fetch(); // Call the dog specific function
}
void walk() override
{
// TODO: Walk
}
void fetch()
{
// TODO: Fetch
}
};
struct cat : public animal
{
void playWidth() override
{
walk(); // Call the common function
climb(); // Call the cat specific function
}
void walk() override
{
// TODO: Walk
}
void climb()
{
// TODO: Climb
}
};
In the example above I leave the common walk
function as a pure virtual function in the base class, so that all child-classes needs to override it. If not all child-classes should have a walk
function, then it could be make class-specific just like fetch
and climb
.
Now instead of your global void playWith(Animal*)
function, just call a->playWidth();
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论