关于c ++:具体类和抽象类有什么区别?

What is the difference between a concrete class and an abstract class?

我正在学习C++,但是我对抽象类和具体类感到困惑。一些现实世界的例子会受到赞赏。


抽象类是一个声明了一个或多个方法但没有定义的类,这意味着编译器知道这些方法是类的一部分,但不知道要为该方法执行什么代码。这些方法称为抽象方法。下面是一个抽象类的例子。

1
2
3
4
class shape {
public:
  virtual void draw() = 0;
};

这将声明一个抽象类,该类指定如果类要具体化,则类的任何子代都应实现draw方法。您不能实例化这个类,因为它是抽象的,毕竟,如果您调用了成员绘制,编译器就不知道要执行什么代码。因此,您不能执行以下操作:

1
2
shape my_shape();
my_shape.draw();

要实际使用draw方法,您需要从此抽象类派生类,该类确实实现了draw方法,使类具体化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class circle : public shape {
public:
  circle(int x, int y, int radius) {
    /* set up the circle */
  }
  virtual draw() {
    /* do stuff to draw the circle */
  }
};

class rectangle : public shape {
public:
  rectangle(int min_x, int min_y, int max_x, int max_y) {
    /* set up rectangle */
  }
  virtual draw() {
    /* do stuff to draw the rectangle */
  }
};

现在可以实例化具体的对象圆和矩形,并使用它们的绘制方法:

1
2
3
4
circle my_circle(40, 30, 10);
rectangle my_rectangle(20, 10, 50, 15);
my_circle.draw();
my_rectangle.draw();

现在的问题当然是,你为什么要这样做?难道你就不能定义圆和矩形类,去掉整个形状类吗?你可以,但这样你就不能利用他们的遗产:

1
2
3
4
std::vector<shape*> my_scene;
my_scene.push_back(new circle(40, 30, 10));
my_scene.push_back(new rectangle(20, 10, 50, 15));
std::for_each(my_scene.begin(), my_scene.end(), std::mem_fun_ref(&shape::draw)

这个代码让您将所有形状收集到一个容器中。如果场景中有许多形状和许多不同的形状,这将使其变得更容易。例如,我们现在可以一次画出所有形状,这样做的代码甚至不需要知道我们拥有的不同类型的形状。

最后,我们需要知道为什么形状的绘制函数是抽象的,而不仅仅是一个空函数,也就是我们为什么不定义:

1
2
3
4
5
6
class shape {
public:
  virtual void draw() {
    /* do nothing */
  }
};

这样做的原因是我们不希望对象的类型是形状的,不管怎样,它们都不是真实的,它们是抽象的。所以为draw方法定义一个实现是没有意义的,即使是一个空的方法。使shape类抽象可以防止我们错误地实例化shape类,或者错误地调用基类的空draw函数而不是派生类的draw函数。实际上,我们为任何希望表现得像形状的类定义了一个接口,我们认为任何此类类都应该有一个draw方法,该方法看起来像我们已经指定的那样。

为了回答您最后一个问题,没有任何类似"普通派生类"的东西,每个类都是抽象的或具体的。具有任何抽象方法的类是抽象的,任何不具体的类都是抽象的。这只是区分两种类型的类的一种方法。基类可以是抽象的或具体的,派生类可以是抽象的或具体的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class abstract_base {
public:
  virtual void abstract_method1() = 0;
  virtual void abstract_method2() = 0;
};

class concrete_base {
public:
  void concrete_method1() {
    /* do something */
  }
};

class abstract_derived1 : public abstract_base {
public:
  virtual void abstract_method3() = 0;
};

class abstract_derived2 : public concrete_base {
public:
  virtual void abstract_method3() = 0;
};

class abstract_derived3 : public abstract_base {
public:
  virtual abstract_method1() {
    /* do something */
  }
  /* note that we do not provide an implementation for
     abstract_method2 so the class is still abstract */

};

class concrete_derived1 : public concrete_base {
public:
  void concrete_method2() {
    /* do something */
  }
};

class concrete_derived2 : public abstract_base {
public:
  virtual void abstract_method1() {
    /* do something */
  }
  virtual void abstract_method2() {
    /* do something */
  }
  /* This class is now concrete because no abstract methods remain */
};


抽象类不能用于创建对象。然而,具体的类可以用来创建一个对象。

具体的意思是"存在于现实或真实经验中;通过感官感知;真实的"。然而,抽象的意思是"不适用的或实际的;理论的"。

无法实例化抽象类。然而,一个具体的可以。

抽象类是具有一个或多个纯虚函数的类。而具体类没有纯粹的虚函数。


具体类是可用于创建对象的类。抽象类不能用于创建对象(必须扩展抽象类并使具体类能够随后创建对象)。

假设有一台机器可以"冲压"原材料并制造汽车。压模是一个具体的类。由此我们可以创建汽车对象。抽象类将是stamper的蓝图。你不能用stamper的蓝图来制造汽车,你需要先用蓝图来制造stamper类。


在这些其他问题中涉及了很多这方面的内容:

  • 什么是抽象类?
  • 抽象类与接口
  • 面向对象编程中的抽象数据类型是什么?
  • 用于具体类的独立头文件-C++

  • 抽象类不能实例化,而具体类可以。抽象类充当派生类(可以实例化的类)的"蓝图"。

    例如,Car类(摘要),而Audi S4类(派生自Car类)是具体实现。


    基类与派生类是抽象类与具体类的正交概念。

    基类是不从任何其他类继承的类。派生类不从其他类继承。

    抽象类是具有一个或多个纯虚拟函数的类。具体类没有纯虚拟。

    抽象类可以是基类,也可以是派生类(它是从另一个抽象类派生的)。具体类也可以是基类或派生类。甚至可以通过向派生类添加纯虚拟函数,从具体类派生抽象类。但在一般情况下,有一个基本抽象类和一个或多个具体的派生类。


    使用抽象类的一个很好的例子是当您构建一个非常模块化的类时。假设您正在使用一个数据存储,但该数据可能位于MySQL数据库、SQLite数据库、XML文件或纯文本中。为了在代码中保持这种通用性,可以创建一个类AbstractDatastore,该类定义要用于从数据存储中获取信息的公共方法。然后,创建您的AbstractDatastore的具体实现,例如XmlDatastoreSQLiteDatastore等。然后,您的程序只需要知道它正在获取AbstractDatastore,它必须具有AbstractDatastore中定义的那些函数,但它不知道或不关心数据是如何存储或检索的。


    混凝土类实现了它的所有方法。抽象其所有方法,除了一些(至少一个)未实现的方法,以便您可以扩展它并实现未实现的方法。

    优点:通过从抽象类扩展,您可以获得基类的所有功能,并且您将"被迫"实现未实现的方法。所以类的设计者基本上是强迫你在类对你有用之前用抽象方法写代码。


    C++ FAQ Lite是一个很好的网站来寻找关于这类问题的答案。

    At the design level, an abstract base class (ABC) corresponds to an abstract concept. If you asked a mechanic if he repaired vehicles, he'd probably wonder what kind-of vehicle you had in mind. Chances are he doesn't repair space shuttles, ocean liners, bicycles, or nuclear submarines. The problem is that the term"vehicle" is an abstract concept (e.g., you can't build a"vehicle" unless you know what kind of vehicle to build). In C++, class Vehicle would be an ABC, with Bicycle, SpaceShuttle, etc, being derived classes (an OceanLiner is-a-kind-of-a Vehicle). In real-world OO, ABCs show up all over the place

    抽象类是具有一个或多个纯虚拟成员函数的类。不能创建抽象类的对象(实例)

    1
    2
    3
    4
    5
     class Shape {
     public:
       virtual void draw() const = 0;  // = 0 means it is"pure virtual"
       ...
     };