C++中virtual关键字解决了什么问题(三)

一、抽象
C++中通过类实现了面向对象的三大特性之一——封装。在实现封装的过程中离不开抽象,那么什么是抽象呢?百度百科的解释是:抽象是从众多的事物中抽取出共同的、本质性的特征,而舍弃其非本质的特征的过程。简单理解就是从多个事物中寻找其共同点并提炼概括的过程。
这里就拿初中学习的平面几何图形来举个简单例子吧。

class Cirle
{
public:
    Cirle(string str = "NewCirle"):name(str){}
    ~Cirle(){}
    int getArea(){return PI*r*r;}; //求面积
    int getPerimeter(){return 2*PI*r;} //求周长
    string getName(){return name;}
    //这里仅作示意就不贴上set方法了,下同
private:
    const string name;    //名称
    int r;          //半径
};
class Rectangle
{
public:
    Rectangle(string str = "NewRectangle"):name(str){}
    ~Rectangle(){}
    int getArea(){return h*w;};
    int getPerimeter(){return 2*(w+h);}
    string getName(){return name;}
private:
    const string name;    //名称
    int w;          //宽
    int h;          //高
};
class Triangle
{
public:
    Triangle(string str = "NewTriangle"):name(str){}
    ~Triangle(){}
    int getArea(){return a*h/2;};
    int getPerimeter(){return a+b+c;}
    string getName(){return name;}
private:
    const string name;    //名称
    int a;          //底
    int b;
    int c;
    int h;          //高
};

我们对这三种图形各自的对象分别抽象出了这三个类,观察这三个类我们发现有诸多共有的方法和属性,这也就意味着,除了在对象这一层面抽象外,我们还可以在类这一层面继续抽象,最终抽象出的结果就是抽象类。
二、抽象类(接口)
普通的类(具体类)是对实例对象的抽象,而抽象类则是对普通类的抽象。抽象类其目的是为了描述类的行为和功能而不必去关注实现。针对上面的三个类再次抽象得到一个新的类作为这三种图形类的基类:

class Shape
{
public:
    Shape(string str):name(str){}
    ~Shape(){}
    virtual int getArea()=0;  //纯虚函数
    virtual int getPerimeter()=0;
    string getName(){return name;}
private:
    const string name;    //名称
};

由于并不知道具体是什么形状,我们不能确切的给出图形和面积的计算方法,但这两个方法是所有图形应具备的,为了最大限度的抽象,我们可以将确定存在但不确定实现的方法通过增加virtual关键字和“=0”将其声明为纯虚函数,对这一类的class的基本方法做出规定,方便后续的扩展,提高代码复用性。经过再次抽象后,三种图形各自的类代码如下:

class Cirle : public Shape
{
public:
    Cirle(string str = "NewCirle"):Shape(str){}
    ~Cirle(){}
    int getArea(){return PI*r*r;};
    int getPerimeter(){return 2*PI*r;}
private:
    int r;          //半径
};
class Rectangle : public Shape
{
public:
    Rectangle(string str = "NewRectangle"):Shape(str){}
    ~Rectangle(){}
    int getArea(){return h*w;};
    int getPerimeter(){return 2*(w+h);}
private:
    int w;          //宽
    int h;          //高
};
class Triangle : public Shape
{
public:
    Triangle(string str = "NewTriangle"):Shape(str){}
    ~Triangle(){}
    int getArea(){return a*h/2;};
    int getPerimeter(){return a+b+c;}
private:
    int a;          //底
    int b;
    int c;
    int h;          //高
};

这样一来就少去了大部分重复的代码,使代码更加简洁。后续增加其他形状的类,只需继承Shape类,实现纯虚函数即可。
三、思维扩展
抽象类也是类,它能实例化对象吗?为什么?
实际操作试试看有什么结果:

int main(int argc, char* argv[])
{
    Cirle c;
    Rectangle r;
    Triangle t;
    Shape s; //用抽象类实例化对象
    cout << c.getName() << endl;
    cout << r.getName() << endl;
    cout << t.getName() << endl;
    return 0;
}

编译:

make
gcc main.cpp -Wall -Werror -m64 -g -c -o main.o
main.cpp: In function ‘int main(int, char**)’:
main.cpp:216:11: error: cannot declare variable ‘s’ to be of abstract type ‘Shape’
     Shape s("NewShape");不能声明对象s为抽象类型Shape,因为下面的虚函数是纯的
           ^
main.cpp:123:7: note:   because the following virtual functions are pure within ‘Shape’:
 class Shape
       ^~~~~
main.cpp:128:17: note: 	virtual int Shape::getArea()
     virtual int getArea()=0;
                 ^~~~~~~
main.cpp:129:17: note: 	virtual int Shape::getPerimeter()
     virtual int getPerimeter()=0;
                 ^~~~~~~~~~~~
Makefile:7: recipe for target 'main.o' failed
make: *** [main.o] Error 1

编译器不允许抽象类实例化对象。即便继承了抽象类之后,在没有实现所有的纯虚函数之前,该子类仍是一个抽象类。
从现实角度来讲,抽象通常意味着不具体,细节不明确,所以抽象类也不能准确的描述一个对象,自然是不能实例化对象的。
未完待续...

posted on 2022-03-04 21:41  OrangeGLC  阅读(84)  评论(0)    收藏  举报

导航