简体   繁体   English

为什么我们需要C ++中的抽象类?

[英]Why do we need abstract classes in C++?

I've just learned about polymorphism in my OOP Class and I'm having a hard time understanding how abstract base classes are useful. 我刚刚在OOP类中了解了多态性,并且我很难理解抽象基类是如何有用的。

What is the purpose of an abstract class? 抽象类的目的是什么? What does defining an abstract base class provide that isn't provided by creating each necessary function in each actual class? 定义一个抽象基类提供了什么,而不是通过在每个实际类中创建每个必需的函数来提供?

The purpose of an abstract class is to define a common protocol for a set of concrete subclasses. 抽象类的目的是为一组具体子类定义公共协议。 This is useful when defining objects that share code, abstract ideas, etc. 这在定义共享代码,抽象概念等的对象时很有用。

Abstract classes have no instances. 抽象类没有实例。 An abstract class must have at least one deferred method (or function). 抽象类必须至少有一个延迟方法(或函数)。 To accomplish this in C++, a pure virtual member function is declared but not defined in the abstract class: 要在C ++中实现此目的,将声明纯虚拟成员函数,但未在抽象类中定义:

class MyClass {
    virtual void pureVirtualFunction() = 0;
}

Attempts to instantiate an abstract class will always result in a compiler error. 尝试实例化抽象类将始终导致编译器错误。

"What does defining an abstract base class provide that isn't provided by creating each necessary function in each actual class?" “通过在每个实际的类中创建每个必需的函数,不能提供定义抽象基类的内容吗?”

The main idea here is code reuse and proper partitioning across classes. 这里的主要思想是代码重用和跨类的适当分区。 It makes more sense to define a function once in a parent class rather than defining over and over again in multiple subclasses: 在父类中定义一次函数更有意义,而不是在多个子类中反复定义:

class A {
   void func1();
   virtual void func2() = 0;
}

class B : public A {
   // inherits A's func1()
   virtual void func2();   // Function defined in implementation file
}

class C : public A {
   // inherits A's func1()
   virtual void func2();   // Function defined in implementation file
}

Having an abstract class like "Dog" with a virtual method like "bark" allows all classes that inherit from Dog to have their bark code called in the same way, even though the Beagle's bark is implemented way differently than the Collie's. 像“Dog”这样的抽象类具有像“bark”这样的虚拟方法,允许所有继承自Dog的类以相同的方式调用它们的树皮代码,即使Beagle的树皮的实现方式与Collie的不同。

Without a common abstract parent (or at least a common parent with a bark virtual method) it'd be difficult to do the following: 如果没有共同的抽象父级(或者至少是具有树皮虚拟方法的公共父级),则很难执行以下操作:

Have a Vector of type Dog that contains Collies, Beagles, German Shepherds etc and make each of them bark. 有一个类型狗的矢量包含牧羊犬,比格犬,德国牧羊犬等,并使他们每个人都吠叫。 With a Vector of Dogs that contains Collies, Beagles, German Shepherds all you would have to do to make them all bark is to iterate through in a for loop and call bark on each one. 使用包含牧羊犬,小猎犬,德国牧羊犬的狗的矢量,你只需要在一个for循环中迭代并在每个上面调用树皮。 Otherwise you'd have to have a separate Vector of Collies, Vector of Beagles etc. 否则你必须有一个单独的矢量Collies,矢量Beagles等。

If the question is "why make Dog abstract when it could be concrete, have a virtual bark defined with a default implementation that can be overriden?", the answer would be that this may be acceptable sometimes -- but, from a design perspective, there really isn't any such thing as a Dog that isn't a Collie or a Beagle or some other breed or mix so although they are all Dogs, there is not one of them in reality that is a Dog but not some other derived class too. 如果问题是“为什么让Dog抽象,当它可能是具体的,有一个虚拟树皮定义了一个可以覆盖的默认实现?”,答案是这有时可以接受 - 但是,从设计的角度来看,实际上没有任何像狗不是牧羊犬或小猎犬或其他品种或混合物的东西,所以虽然它们都是狗,但实际上没有一只狗是狗而不是其他一些衍生物上课也是。 Also, since dogs barking is so varied from one breed to another, there is unlikely to be any real acceptable default implementation of bark that would be acceptable for any decent group of Dogs. 此外,由于狗的吠叫从一个品种到另一个品种是如此多样化,因此不可能有任何真正可接受的默认实施树皮,这对于任何体面的狗群都是可接受的。

I hope this helps you understand the purpose: yes, you're going to have to implement bark in each subclass anyway, but the common abstract ancestor lets you treat any subclass as a member of a base class and invoke behaviors that may be conceptually similar like bark but in fact have very different implementations. 我希望这有助于您理解目的:是的,您将不得不在每个子类中实现bark,但是通用的抽象祖先允许您将任何子类视为基类的成员并调用可能在概念上类似的行为像树皮,但实际上有不同的实施。

Abstract classes allow for compile time protocol enforcement. 抽象类允许编译时协议实施。 These protocols define what it means to be a part of a class family. 这些协议定义了成为一个类家庭的一部分意味着什么。

Another way to think of it is that a abstract class is a contract that your implementing classes must fulfill. 另一种思考方式是抽象类是您的实现类必须满足的契约。 If they do not fulfill this contract they cannot be part of the class family and they must be modified to conform to the contract. 如果他们不履行本合同,则他们不能成为集体家庭的一部分,必须对其进行修改以符合合同。 The provided contract may provide default functionality, but it also leaves it up to the sub-class to define more specific or different functionality while still remaining within the scope of the contract. 提供的合同可以提供默认功能,但它也可以由子类来定义更具体或不同的功能,同时仍然属于合同范围。

For small projects this may not seem useful but for large projects it provides conformity and structure as it provides documentation through the abstract class contract. 对于小项目,这似乎没有用,但对于大型项目,它提供了符合性和结构,因为它通过抽象类合同提供文档。 This makes for more maintainable code and makes for the sub-classes to each have the same protocol making using and developing new sub-classes easier. 这使得代码更易于维护,并使每个子类具有相同的协议,从而更容易地使用和开发新的子类。

I have a dog. 我养了一条狗。 Abstract class dog with a method bark. 与方法吠声的抽象类狗。 My particular dog makes one bark. 我特别的狗做了一个树皮。 Other dogs bark in a different way. 其他狗以不同的方式吠叫。 So defining a dog in the abstract way is useful. 因此,以抽象方式定义狗是有用的。

The purpose of an abstract class is to provide an appropriate base class from which other classes can inherit. 抽象类的目的是提供一个适当的基类,其他类可以从中继承。 Abstract classes cannot be used to instantiate objects and serves only as an interface. 抽象类不能用于实例化对象,仅用作接口。 Attempting to instantiate an object of an abstract class causes a compilation error. 尝试实例化抽象类的对象会导致编译错误。 (because vtable entry is not filled with memory location for virtual function we mentioned in Abstract Class) (因为vtable条目没有填充我们在Abstract Class中提到的虚函数的内存位置)

Thus, if a subclass of an ABC needs to be instantiated, it has to implement each of the virtual functions, which means that it supports the interface declared by the ABC. 因此,如果需要实例化ABC的子类,则必须实现每个虚函数,这意味着它支持ABC声明的接口。 Failure to override a pure virtual function in a derived class, then attempting to instantiate objects of that class, is a compilation error. 未能覆盖派生类中的纯虚函数,然后尝试实例化该类的对象,是编译错误。

Example: 例:

class mobileinternet
{
public:
virtual enableinternet()=0;//defines as virtual so that each class can overwrite
};


class 2gplan : public mobileinternet

{
    private:

         int providelowspeedinternet(); //logic to give less speed.

    public:

         void enableinternet(int) {
                                     // implement logic
                                 }

};

//similarly

class 3gplan : public enableinternet
{
   private: high speed logic (different then both of the above)

   public: 
          /*    */
}

here in this example, you can understand. 在这个例子中,你可以理解。

Abstract classes are used to define an interface to be implemented. 抽象类用于定义要实现的接口。 See some references: 看一些参考文献:

http://en.wikibooks.org/wiki/C%2B%2B_Programming/Classes/Abstract_Classes http://en.wikibooks.org/wiki/C%2B%2B_Programming/Classes/Abstract_Classes

An abstract class AbstractClass as a base class is needed when there is functionality that is desired for all objects that have a type deriving from AbstractClass , but cannot sensibly be implemented on the AbstractClass itself. 当具有从AbstractClass派生类型的所有对象所需的功能时,需要抽象类AbstractClass作为基类,但不能在AbstractClass本身上明智地实现。

The old and somewhat artificial OO example of having a base class Vehicle with derived classes Car , Motorcycle , ... provides a good example here, say you want a method move() - you can implement the way that a Car or a Motorcycle moves, but Vehicle s don't move in a generic way, so Vehicle::move() will have to be pure virtual and Vehicle therefore abstract. 旧的和有点人工的OO示例有一个基类Vehicle派生类CarMotorcycle ,...这里提供了一个很好的例子,比如你想要一个方法move() - 你可以实现CarMotorcycle移动的方式但是, Vehicle不会以通用方式移动,因此Vehicle::move()必须是纯虚拟的,因此Vehicle必须是抽象的。

why don't we create each necessary function in each class ? 为什么我们不在每个班级创造每个必要的功能? (C++) (C ++)

You have to create each necessary function marked as abstract in each derived class. 您必须在每个派生类中创建标记为abstract每个必要函数。

If you question is, why to create abstract function in abstract class? 如果您有疑问,为什么要在抽象类中创建抽象函数?

It allows strict run time polymorphism . 它允许严格的运行时多态性

Also read Interface vs Abstract Class (general OO) 同时阅读接口与抽象类(通用OO)

abstract class dog
{
bark();
}

// function inside another module

dogbarking(dog obj)
{
   dog.bark(); // function will call depend up on address inside the obj
}


// our class
ourclass: inherit dog
{
    bark()
    {
         //body
     }
}


main()
{
    ourclass obj;
    dogbarking(obj);
}

we can see that dogbarking is a function written in another module. 我们可以看到dogbarking是另一个模块中编写的函数。 it knows only the abstract class dog. 它只知道抽象类狗。 even though it can call the function bark inside ourclass. 即使它可以在我们的类中调用函数bark。 in main function we create object of ourclass and pass to function dogbarking where it received using reference object of abstract class dog. 在main函数中我们创建了我们类的对象并传递给函数dogbarking,它使用抽象类dog的引用对象接收它。

Imagine you have two methods for displaying a string: 想象一下,您有两种显示字符串的方法:

DisplayDialog(string s);
PrintToConsole(string s);

And you want to write some code that can be switched between these two methods: 并且您想要编写一些可以在这两种方法之间切换的代码:

void foo(bool useDialogs) {
    if (useDialogs) {
        DisplayDialog("Hello, World!");
    } else {
        PrintToConsole("Hello, World!");
    }

    if (useDialogs) {
        DisplayDialog("The result of 2 * 3 is ");
    } else {
        PrintToConsole("The result of 2 * 3 is ");
    }

    int i = 2 * 3;
    string s = to_string(i);

    if (useDialogs) {
        DisplayDialog(s);
    } else {
        PrintToConsole(s);
    }        
}

This code is tightly coupled to the specific methods used for displaying the string. 此代码与用于显示字符串的特定方法紧密耦合。 Adding an additional method, changing how the method is selected, etc. will affect every piece of code that uses this. 添加其他方法,更改方法的选择方式等将影响使用此方法的每一段代码。 This code is tightly coupled to the set of methods we use to display strings. 此代码我们用于显示字符串的方法集紧密耦合

Abstract base classes are a way of decoupling code that uses some functionality from the code that implements that functionality. 抽象基类是一种解耦代码的方法,它使用实现该功能的代码中的某些功能。 It does this by defining a common interface to all the various ways of doing the task. 它通过为所有执行任务的各种方式定义一个公共接口来实现此目的。

class AbstractStringDisplayer {
public:
    virtual display(string s) = 0;

    virtual ~AbstractStringDisplayer();
};

void foo(AbstractStringDisplayer *asd) {
    asd->display("Hello, World!");
    asd->display("The result of 2 * 3 is ");

    int i = 2 * 3;
    string s = to_string(i);

    asd->display(s);
}

int main() {
    AbstractStringDisplayer *asd = getStringDisplayerBasedOnUserPreferencesOrWhatever();

    foo(asd);
}

Using the interface defined by AbstractStringDisplayer we can create and use as many new ways of displaying strings as we want, and code that uses the abstract interface won't need to be changed. 使用AbstractStringDisplayer定义的接口,我们可以创建和使用尽可能多的新方法来显示字符串,并且不需要更改使用抽象接口的代码。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM