c++中为什么需要抽象类?

Why do we need abstract classes in C++?

本文关键字:抽象类 为什么 c++      更新时间:2023-10-16

我刚刚了解了我的OOP类中的多态性,我很难理解抽象基类是如何有用的。

抽象类的目的是什么?定义抽象基类提供了哪些在每个实际类中创建每个必要函数所不能提供的功能?

抽象类的目的是为一组具体子类定义一个公共协议。这在定义共享代码、抽象概念等的对象时非常有用。

抽象类没有实例。抽象类必须至少有一个延迟方法(或函数)。要在c++中实现这一点,在抽象类中声明纯虚成员函数,但不定义:

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

尝试实例化抽象类总是会导致编译器错误。

"定义一个抽象基类提供了什么没有提供的通过在每个实际类中创建每个必要的函数?"

这里的主要思想是代码重用和适当的跨类分区。在父类中定义一次函数比在多个子类中一次又一次地定义函数更有意义:

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
}

使用一个抽象类如"Dog"和一个虚拟方法如"bark"允许所有从Dog继承的类以相同的方式调用它们的吠叫代码,即使比格犬的吠叫与柯利犬的吠叫的实现方式不同。

如果没有一个通用的抽象父类(或者至少是一个带有虚拟方法的通用父类),将很难做到以下事情:

有一个类型为Dog的Vector,其中包含Collies, Beagles, German shepherd等,并让它们各自吠叫。对于包含Collies, Beagles, German shepherd的Vector of Dogs,你所需要做的就是在for循环中进行迭代,并对每个狗调用bark。否则,你必须有一个单独的柯利牧羊犬矢量,比格尔猎犬矢量等。

如果问题是"为什么让狗抽象时可以是具体的,有一个虚拟的树皮定义了一个默认实现,可以重载吗?",答案是,有时这是可以接受的,但是,从设计的角度来看,实在是没有任何所谓的狗不是一个牧羊犬或小猎犬或其他品种或组合虽然都是狗,没有其中一个在现实中是一条狗,但不是其他派生类。而且,由于狗的吠叫在不同的品种之间是如此的不同,所以不太可能有任何真正可接受的吠叫的默认实现,可以被任何体面的狗群所接受。

我希望这有助于你理解目的:是的,无论如何你都必须在每个子类中实现bark,但是公共抽象祖先允许你将任何子类视为基类的成员,并调用可能在概念上类似于bark的行为,但实际上有非常不同的实现。

抽象类允许编译时执行协议。这些协议定义了成为类族的一部分意味着什么。

另一种思考方法是,抽象类是实现类必须完成的契约。如果他们没有履行这个合同,他们就不能成为班级家庭的一部分,他们必须被修改以符合合同。所提供的契约可以提供默认功能,但它也将其留给子类来定义更具体或不同的功能,同时仍然保持在契约的范围内。

对于小项目来说,这似乎没什么用,但对于大项目来说,它提供了一致性和结构,因为它通过抽象类契约提供了文档。这使得代码更易于维护,并且使得每个子类都具有相同的协议,从而使使用和开发新子类更容易。

抽象类的目的是提供一个合适的基类,其他类可以从这个基类继承。抽象类不能用于实例化对象,只能作为接口使用。尝试实例化抽象类的对象会导致编译错误。(因为虚函数表项没有被我们在抽象类中提到的虚函数的内存位置填充)

因此,如果需要实例化ABC的子类,它必须实现每个虚函数,这意味着它支持由ABC声明的接口。如果在派生类中重写纯虚函数失败,然后试图实例化该类的对象,则会导致编译错误。

的例子:

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: 
          /*    */
}

在这个例子中,你可以理解。

我有一只狗。抽象类狗与方法吠。我的狗只会叫一声。其他狗叫的方式不同。所以用抽象的方式定义狗是有用的。

抽象类用于定义要实现的接口。参考文献:

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

一个抽象类AbstractClass作为一个基类是需要的,当有一个功能是所有的对象都希望从AbstractClass派生的类型,但不能明智地实现在AbstractClass本身。

旧的和有点人为的OO示例,具有基类Vehicle和派生类Car, Motorcycle,…这里提供了一个很好的例子,假设你想要一个方法move() -你可以实现CarMotorcycle移动的方式,但Vehicle不会以通用的方式移动,所以Vehicle::move()必须是纯虚拟的,因此Vehicle是抽象的。

为什么不在每个类中创建每个必要的函数呢?(c++)

您必须在每个派生类中创建标记为abstract的每个必要函数。

如果你的问题是,为什么要在抽象类中创建抽象函数?

允许严格的运行时多态性

也阅读接口与抽象类(一般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);
}

我们可以看到dogbarking是在另一个模块中编写的函数。它只知道抽象类狗。即使它可以在我们的类中调用bark函数。在main函数中,我们创建了我们类的对象,并传递给函数dogbarking,在那里它使用抽象类dog的引用对象接收。

假设您有两个显示字符串的方法:

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

你想写一些可以在这两个方法之间切换的代码:

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);
    }        
}

此代码与用于显示字符串的特定方法紧密耦合。添加一个额外的方法,改变方法的选择方式,等等,都会影响到使用它的每一段代码。这段代码与我们用来显示字符串的方法集紧密耦合。

抽象基类是一种将使用某些功能的代码与实现该功能的代码解耦的方法。它通过为执行任务的所有不同方法定义一个公共接口来实现这一点。
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);
}

使用abstractstringdisplay定义的接口,我们可以创建和使用任意多的显示字符串的新方法,并且不需要更改使用抽象接口的代码。