简体   繁体   English

从具体类派生抽象类

[英]Deriving an abstract class from concrete class

Let's say we have a concrete class Apple . 假设我们有一个具体的class Apple (Apple objects can be instantiated.) Now, someone comes and derives an abstract class Peach from Apple. (可以实例化Apple对象。)现在,有人来从Apple派生抽象class Peach It's abstract because it introduces a new pure virtual function. 它是抽象的,因为它引入了新的纯虚函数。 The user of Peach is now forced to derive from it and define this new function. 现在,Peach的用户被迫从中派生并定义此新功能。 Is this a common pattern? 这是常见的模式吗? Is this correct to do? 这是正确的吗?

Sample: 样品:


class Apple
{
public:
    virtual void MakePie();
    // more stuff here
};

class Peach : public Apple { public: virtual void MakeDeliciousDesserts() = 0; // more stuff here };

Now let's say we have a concrete class Berry . 现在,我们有一个具体的class Berry Someone derives an abstract class Tomato from Berry. 有人从浆果派生了抽象class Tomato It's abstract because it overwrites one of Berry's virtual functions, and makes it pure virtual. 它是抽象的,因为它会覆盖Berry的虚拟功能之一,并使之成为纯虚拟的。 The user of Tomato has to re-implement the function previously defined in Berry. Tomato的用户必须重新实现Berry中先前定义的功能。 Is this a common pattern? 这是常见的模式吗? Is this correct to do? 这是正确的吗?

Sample: 样品:

 class Berry { public: virtual void EatYummyPie(); // more stuff here }; 

\n\n

class Tomato : public Berry { public: virtual void EatYummyPie() = 0; // more stuff here };

Note: The names are contrived and do not reflect any actual code (hopefully). 注意:名称是人为设计的,并且不反映任何实际代码(希望如此)。 No fruits have been harmed in the writing of this question. 在撰写这个问题时没有任何成果受到损害。

Re Peach from Apple: 来自苹果的Re Peach:

  • Don't do it if Apple is a value class (ie has copy ctor, non-identical instances can be equal, etc). 如果Apple是值类(例如,具有复制ctor,不相同的实例可以相等,等等),则不要执行此操作。 See Meyers More Effective C++ Item 33 for why. 有关原因,请参见Meyers更有效的C ++项目33。
  • Don't do it if Apple has a public nonvirtual destructor, otherwise you invite undefined behaviour when your users delete an Apple through a pointer to Peach. 如果Apple有公共的非虚拟析构函数,则不要执行此操作,否则当用户通过指向Peach的指针删除Apple时,您会引发未定义的行为。
  • Otherwise, you're probably safe, because you haven't violated Liskov substitutability . 否则,您可能很安全,因为您没有违反Liskov替代性 A Peach IS-A Apple. 桃IS-A苹果。
  • If you own the Apple code, prefer to factor out a common abstract base class (Fruit perhaps) and derive Apple and Peach from it. 如果您拥有Apple代码,则更喜欢分解出一个通用的抽象基类(也许是Fruit)并从中派生Apple和Peach。

Re Tomato from Berry: 来自浆果的Re Tomato:

  • Same as above, plus: 与上述相同,另外:
  • Avoid, because it's unusual 避免,因为这很不寻常
  • If you must, document what derived classes of Tomato must do in order not to violate Liskov substitutability. 如果必须的话,记录下Tomato的派生类必须做什么才能不违反Liskov的可替代性。 The function you are overriding in Berry - let's call it Juice() - imposes certain requirements and makes certain promises. 在Berry中您要重写的功能-我们称它为Juice() -提出了一定的要求并做出了一定的承诺。 Derived classes' implementations of Juice() must require no more and promise no less. 派生类的Juice()必须没有更多要求,也不会更少承诺。 Then a DerivedTomato IS-A Berry and code which only knows about Berry is safe. 然后,仅知道Berry的DerivedTomato IS-A Berry和代码是安全的。

Possibly, you will meet the last requirement by documenting that DerivedTomatoes must call Berry::Juice() . 通过记录DerivedTomatoes必须调用Berry::Juice() ,可能会满足最后一个要求。 If so, consider using Template Method instead: 如果是这样,请考虑改用Template Method:

class Tomato : public Berry
{
public:
    void Juice() 
    {
        PrepareJuice();
        Berry::Juice();
    }
    virtual void PrepareJuice() = 0;
};

Now there is an excellent chance that a Tomato IS-A Berry, contrary to botanical expectations. 现在,极有可能番茄IS-A浆果违反了植物学的预期。 (The exception is if derived classes' implementations of PrepareJuice impose extra preconditions beyond those imposed by Berry::Juice ). (例外是,如果派生类的PrepareJuice的实现强加了Berry::Juice施加的前提以外的其他前提)。

It would seem to me like an indication of a bad design. 在我看来,这似乎表明设计不好。 Could be forced if you wanted to take a concrete definition from a closed library and extend it and branch a bunch of stuff off it, but at that point I'd be seriously considering the guideline regarding Encapsulation over Inheritance.. If you possibly can encapsulate, you probably should. 如果您想从一个封闭的库中获取一个具体的定义并对其进行扩展,并从中分支出很多东西,可能会被强制执行,但是那时候我将认真考虑有关继承封装的准则。 ,您可能应该。

Yeah, the more I think about it, this is a Very Bad Idea. 是的,我想得越多,这是一个非常糟糕的主意。

Not necessarily wrong , but definitely smelly. 不一定是错误的 ,但肯定是臭的。 Especially if you leave the fruit out in the sun for too long. 特别是如果您将水果放在阳光下太长时间。 (And I don't think my dentist would like me eating concrete apples.) (而且我认为我的牙医不希望我吃水泥苹果。)

Though, the main thing I see here that's smelly isn't so much the abstract class derived from a concrete class, but the REALLY DEEP inheritance hierarchy. 虽然,我在这里看到的主要问题不是从具体类派生的抽象类,而是真正的继承层次。

EDIT: re-reading I see that these are two hierarchies. 编辑:重新阅读我看到这是两个层次结构。 All the fruit stuff got me mixed up. 所有的水果东西使我感到困惑。

If you use the recommended practice of having inheritance model "is-a" then this pattern would pretty much never come up. 如果您使用具有继承模型“ is-a”的推荐做法,则这种模式几乎永远不会出现。

Once you have a concrete class, you are saying that it is something that you can actually create an instance of. 一旦有了具体的类,您就可以说它实际上是您可以创建其实例的东西。 If you then derive an abstract class from it, then something that is an attribute of the base class is not true of the derived class, which should set of klaxons that something's not right. 如果然后从中派生一个抽象类,则作为基类的属性的某些东西对派生类而言并不正确,派生类中的klaxons应该是不正确的。

Looking at your example, a peach is not an apple, so it should not be derived from it. 在您的示例中,桃子不是苹果,因此不应衍生自它。 Same is true for Tomato deriving from Berry. 源自浆果的番茄也是如此。

This is where I would usually advise containment, but that doesn't even seem to be a good model, since an Apple does not contain a Peach. 我通常会在这里建议围堵,但这似乎并不是一个好的模型,因为Apple不包含Peach。

In this case, I would factor out the common interface -- PieFilling or DessertItem. 在这种情况下,我将排除常见的接口-PieFilling或DessertItem。

a bit unusual, but if you had some other subclass of the base class and the subclasses of the abstract class had enough common stuff to justify the existance of the abstract class like: 有点不寻常,但是如果您还有基类的其他子类,并且抽象类的子类具有足够的通用知识来证明抽象类的存在,例如:

class Concrete
{
public:
    virtual void eat() {}
};
class Sub::public Concrete { // some concrete subclass
    virtual void eat() {}
};
class Abstract:public Concrete // abstract subclass
{
public:
    virtual void eat()=0;
    // and some stuff common to Sub1 and Sub2
};
class Sub1:public Abstract {
    void eat() {}
};
class Sub2:public Abstract {
    void eat() {}
};
int main() {
    Concrete *sub1=new Sub1(),*sub2=new Sub2();
    sub1->eat();
    sub2->eat();
    return 0;
}

Hmmm...by thinking "what a....." for a couple of seconds, I come to a conclusion it is not common... Also, I would not derive Peach from Apple and Tomato from Berry...do you have any better example?:) 嗯...通过思考“什么.....”几秒钟,我得出一个结论,这并不常见...而且,我不会从苹果中得到桃子,从浆果中得到番茄...你有更好的例子吗?

It's a lot of weird shit you can do in C++...I can't even think of 1% of it... 您可以在C ++中做很多奇怪的事...我什至都不会想到其中的1%...

About override a virtual with a pure virtual, you can probably just hide it and it will be really weird... 关于使用纯虚拟机覆盖虚拟机,您可能只是将其隐藏,这真是很奇怪。

If you can find a stupid C++ compiler that would link this function as a virtual, then you will get runtime pure virtual function call... 如果您可以找到一个愚蠢的C ++编译器来将该函数链接为虚函数,那么您将获得运行时纯虚函数调用...

I think this can only be done for a hack and I have no idea what kind of hack really... 我认为这只能针对骇客,我不知道到底是哪种骇客...

To answer your first question, you can do this since users of Apple, if given a concrete instance derived from Peach will not know any different. 要回答您的第一个问题,您可以这样做,因为Apple用户(如果给出了派生自Peach的具体实例将不会有任何不同)。 And the instance will not know its not an Apple (unless there are some virtual functions from Apple that are overridden that you didn't tell us about). 并且该实例将不会知道它不是Apple(除非您没有告诉我们有关Apple的一些虚拟功能的说明,这些虚拟功能将被覆盖)。

I can't yet imagine how useful it would be to override a virtual function with a pure virtual one - is that even legal? 我还无法想象用纯虚函数覆盖虚函数会多么有用-甚至合法吗?

In general you want to conform with Scott Meyers "Make all non-leaf classes abstract" item from his books. 通常,您要遵循Scott Meyers的著作中的“使所有非叶类抽象化”项目。

Anyway, apart from that what you describe seems to be legal - its just that I can't see you needing it that often. 无论如何,除了您描述的内容似乎是合法的-只是我看不到您经常需要它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从具体类派生抽象模板类 - Deriving an abstract template class from concrete class 通过从具体类派生来填写抽象类成员 - Filling out abstract class members by deriving from concrete class 从另一个Abstract类派生一个Abstract类时,C ++中的类重新定义错误 - Class redefinition error in C++ while deriving an Abstract Class from another Abstract Class 如果它是从抽象类派生的,则无法使用具体的类指针调用函数 - Not able to call function with concrete class pointer, if it is derived from abstract class 将受保护方法从抽象类更改为私有类 - Change Protected Method From Abstract Class to Private In Concrete Class 从类模板派生一个类 - Deriving a class from a class template 具体类和抽象类之间有什么区别? - What is the difference between a concrete class and an abstract class? 模板数据结构 - 访问从抽象 class 派生的模板 class 的 getter 和 setter - Template data structure - Access to getters and setters of a template class deriving from an abstract class 两个抽象类,派生自相同的基础 class。 如何访问从一个抽象 class 到另一个的指针 - Two abstract classes, deriving from the same base class. How can I access a pointer from one abstract class to the other Diamond继承-从抽象类以及具体类继承而不是实现基于共享的类 - Diamond inheritence - Inheriting from an abstract class and also a concrete class than implements a shared based class
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM