25.抽象类

25.面向对象-多态-抽象类


25.1 知识点

抽象类

  • 抽象类是指包含至少一个纯虚函数的类

  • 基本特点:

    • 不能直接实例化
    • 只能被继承
    • 若子类继承抽象类后不实现纯虚函数,也会变成抽象类
    • 抽象类中可以有成员变量和普通函数

纯虚函数

  • 纯虚函数就是没有被实现的虚函数

  • 语法:

    virtual 返回类型 函数名(参数列表) = 0;
    
  • 纯虚函数一般用来表示抽象的行为

  • 举例说明:

    • 图形基类
    • / | \
    • 圆 正方形 长方形

举例说明

#pragma once
#include <iostream>
using namespace std;

class Fruit
{
private:
    string name;

public:
    Fruit(string name)
    {
        this->name = name;
    }

    virtual ~Fruit()
    {
        
    }

    //纯虚函数 因为不同的水果被吃的方式不同
    virtual void Eat() = 0;

    void Washing()
    {
        cout << "洗水果:" << name << endl;
    }
};
#pragma once
#include "Fruit.h"

class Apple : public Fruit
{
public:
    Apple(string name) : Fruit(name)
    {
        
    }

    void Eat() override
    {
        cout << "直接咬苹果来吃" << endl;
    }
};
#pragma once
#include "Fruit.h"

class Banana : public Fruit
{
public:
    Banana(string name) : Fruit(name)
    {
        
    }

    void Eat() override
    {
        cout << "剥开香蕉吃" << endl;
    }
};

测试调用代码

Fruit* a = new Apple("苹果");
a->Washing();
a->Eat();
delete a;
a = nullptr;

Fruit* b = new Banana("香蕉");
b->Washing();
b->Eat();
delete b;
b = nullptr;

输出:

洗水果:苹果
直接咬苹果来吃
洗水果:香蕉
剥开香蕉吃

25.2 知识点代码

Fruit.cpp

#pragma once
#include <iostream>
using namespace std;
class Fruit
{
private:
    string name;
public:
    Fruit(string name)
    {
        this->name = name;
    }

    virtual ~Fruit()
    {

    }

    //纯虚函数 因为不同的水果被吃的方式不同
    virtual void Eat() = 0;

    void Washing()
    {
        cout << "洗水果:" << name << endl;
    }
};

Apple.cpp

#pragma once
#include "Fruit.h"
class Apple : public Fruit
{
public:
    Apple(string name) : Fruit(name)
    {

    }

    void Eat() override
    {
        cout << "直接咬苹果来吃" << endl;
    }
};

Banana.cpp

#pragma once
#include "Fruit.h"
class Banana : public Fruit
{
public:
    Banana(string name) : Fruit(name)
    {

    }

    void Eat() override
    {
        cout << "剥开香蕉吃" << endl;
    }
};

Lesson25_面向对象_多态_抽象类.cpp

#include <iostream>
#include "Fruit.h"
#include "Apple.h"
#include "Banana.h"
int main()
{
    #pragma region 知识点一 抽象类
    //概念:
    //抽象类是指包含至少一个纯虚函数的类
    //基本特点:
    //1.不能直接实例化
    //2.只能被继承
    //3.若子类继承抽象类后不实现纯虚函数,也会变成抽象类
    //4.抽象类中可以有成员变量和普通函数
    #pragma endregion

    #pragma region 知识点二 纯虚函数
    //纯虚函数就是没有被实现的虚函数
    //语法:
    //virtual 返回类型 函数名(参数列表) = 0;

    //纯虚函数一般用来表示抽象的行为
    //比如:
    //   图形基类
    //  /   |    \
    //圆  正方形  长方形
    #pragma endregion

    #pragma region 知识点三 举例说明
    Fruit* a = new Apple("苹果");
    a->Washing();
    a->Eat();
    delete a;
    a = nullptr;

    Fruit* b = new Banana("香蕉");
    b->Washing();
    b->Eat();
    delete b;
    b = nullptr;

    //洗水果:苹果
    //直接咬苹果来吃
    //洗水果 : 香蕉
    //剥开香蕉吃

    #pragma endregion

}

25.3 练习题

写一个动物抽象类,写三个子类:人叫,狗叫,猫叫

首先,我们定义一个抽象类 Animal,其中包含一个纯虚函数 Speak(),表示发出叫声。同时我们定义一个虚析构函数,确保子类对象通过父类指针释放时能正确析构。

#pragma once
#include <iostream>
using namespace std;

// 抽象类 Animal:定义了一个纯虚函数 Speak 表示发声
class Animal
{
public:
    // 纯虚函数,所有子类必须重写该方法以实现具体发声
    virtual void Speak() = 0;

    // 虚析构函数,确保通过父类指针删除子类对象时资源正确释放
    virtual ~Animal() {}
};

接下来创建三个继承 Animal 的子类:

人类 Person 类

#pragma once
#include "Animal.h"

class Person : public Animal
{
public:
    // 实现 Speak 方法,表示人说“你好”
    void Speak() override
    {
        cout << "你好" << endl;
    }
};

狗类 Dog

#pragma once
#include "Animal.h"

class Dog : public Animal
{
public:
    // 实现 Speak 方法,表示狗叫“汪汪”
    void Speak() override
    {
        cout << "汪汪" << endl;
    }
};

猫类 Cat

#pragma once
#include "Animal.h"

class Cat : public Animal
{
public:
    // 实现 Speak 方法,表示猫叫“喵喵”
    void Speak() override
    {
        cout << "喵喵" << endl;
    }
};

测试代码

Animal* p = new Person();
p->Speak();       // 输出:你好
delete p;
p = nullptr;

Animal* d = new Dog();
d->Speak();       // 输出:汪汪
delete d;
d = nullptr;

Animal* c = new Cat();
c->Speak();       // 输出:喵喵
delete c;
c = nullptr;

创建一个图形类,包含求面积和周长两个方法。创建矩形类、正方形类、圆形类继承图形类,实例化对象并求面积和周长

定义一个抽象类 Graph,包含两个纯虚函数:GetLength()GetArea(),分别表示周长和面积的计算。

#pragma once

class Graph
{
public:
    // 计算图形的周长
    virtual float GetLength() = 0;

    // 计算图形的面积
    virtual float GetArea() = 0;

    virtual ~Graph() {}
};

矩形类 Rect

#pragma once
#include "Graph.h"

class Rect : public Graph
{
public:
    Rect(float w, float h);
    float GetLength() override;
    float GetArea() override;

private:
    float w, h;
};
#include "Rect.h"

Rect::Rect(float w, float h)
{
    this->w = w;
    this->h = h;
}

float Rect::GetLength()
{
    return (w + h) * 2;
}

float Rect::GetArea()
{
    return w * h;
}

正方形类 Square

#pragma once
#include "Graph.h"

class Square : public Graph
{
public:
    Square(float l);
    float GetLength() override;
    float GetArea() override;

private:
    float l;
};
#include "Square.h"

Square::Square(float l)
{
    this->l = l;
}

float Square::GetLength()
{
    return l * 4;
}

float Square::GetArea()
{
    return l * l;
}

圆形类 Circular

#pragma once
#include "Graph.h"

class Circular : public Graph
{
public:
    Circular(float r);
    float GetLength() override;
    float GetArea() override;

private:
    float r;
};
#include "Circular.h"

Circular::Circular(float r)
{
    this->r = r;
}

float Circular::GetLength()
{
    // 圆周长公式:2πr
    return 2 * 3.14f * r;
}

float Circular::GetArea()
{
    // 圆面积公式:πr²
    return 3.14f * r * r;
}

测试代码

Graph* g = new Rect(5, 6);
cout << g->GetLength() << endl;   // 22
cout << g->GetArea() << endl;     // 30
delete g;
g = nullptr;

g = new Square(5);
cout << g->GetLength() << endl;   // 20
cout << g->GetArea() << endl;     // 25
delete g;
g = nullptr;

g = new Circular(3);
cout << g->GetLength() << endl;   // 18.84
cout << g->GetArea() << endl;     // 28.26
delete g;
g = nullptr;

C++ 中抽象类中的纯虚函数在子类中必须被实现吗?

不一定。

需要根据具体情况来判断:

  • 如果子类对象要被实例化,那么必须实现所有继承下来的纯虚函数。
  • 如果子类对象不需要被实例化,子类就可以不实现纯虚函数。

这是因为:

  • 若子类没有实现所有的纯虚函数,它本身也仍然是一个抽象类。抽象类不能被实例化。
  • 只有当子类实现了父类的全部纯虚函数后,才能变成具体类,才能创建其实例。

总结:

  • 子类是否必须实现父类中的纯虚函数,取决于是否需要实例化子类对象。
  • 纯虚函数就是为继承类约定一个“必须实现的接口”,否则子类也会变成抽象类。

25.4 练习题代码

Animal.h

#pragma once
#include <iostream>
using namespace std;
class Animal
{
public:
    virtual void Speak() = 0;

    virtual ~Animal()
    {

    }
};

Person.h

#pragma once
#include "Animal.h"
class Person :
    public Animal
{
public:
    void Speak() override
    {
        cout << "你好" << endl;
    }
};

Dog.h

#pragma once
#include "Animal.h"
class Dog :
    public Animal
{
public:
    void Speak() override
    {
        cout << "汪汪" << endl;
    }
};

Cat.h

#pragma once
#include "Animal.h"
class Cat :
    public Animal
{
public:
    void Speak() override
    {
        cout << "喵喵" << endl;
    }
};

Graph.h

#pragma once
class Graph
{
public:
    virtual float GetLength() = 0;

    virtual float GetArea() = 0;
};

Rect.h

#pragma once
#include "Graph.h"
class Rect :
    public Graph
{
private:
    float w;
    float h;
public:
    Rect(float w, float h);
    float GetLength() override;
    float GetArea() override;
};

Rect.cpp

#include "Rect.h"

Rect::Rect(float w, float h)
{
    this->w = w;
    this->h = h;
}
float Rect::GetLength()
{
    return (w + h) * 2;
}
float Rect::GetArea()
{
    return w * h;
}

Square.h

#pragma once
#include "Graph.h"
class Square :
    public Graph
{
private:
    float l;
public:
    Square(float l);
    float GetLength() override;
    float GetArea() override;
};

Square.cpp

#include "Square.h"


Square::Square(float l)
{
    this->l = l;
}
float Square::GetLength()
{
    return l * 4;
}
float Square::GetArea()
{
    return l * l;
}

Circular.h

#pragma once
#include "Graph.h"
class Circular :
    public Graph
{
private:
    float r;
public:
    Circular(float r);
    float GetLength() override;
    float GetArea() override;
};

Circular.cpp

#include "Circular.h"
Circular::Circular(float r)
{
    this->r = r;
}
float Circular::GetLength()
{
    //2πr
    return 2 * 3.14f * r;
}
float Circular::GetArea()
{
    //πr²
    return 3.14f * r * r;
}

Lesson25_练习题.cpp

#include <iostream>
#include "Person.h"
#include "Dog.h"
#include "Cat.h"
#include "Rect.h"
#include "Square.h"
#include "Circular.h"
using namespace std;
int main()
{
    #pragma region 练习题一 
    /*写一个动物抽象类,写三个子类
    人叫,狗叫,猫叫*/
    Animal* p = new Person();
    p->Speak();
    delete p;
    p = nullptr;

    Animal* d = new Dog();
    d->Speak();
    delete d;
    d = nullptr;

    Animal* c = new Cat();
    c->Speak();
    delete c;
    c = nullptr;

    //你好
    //汪汪
    //喵喵

    #pragma endregion

    #pragma region 练习题二 
    /*创建一个图形类,有求面积和周长两个方法
    创建矩形类,正方形类,圆形类继承图形类
    实例化矩形、正方形、圆形对象求面积和周长*/

    Graph* g = new Rect(5, 6);
    cout << g->GetLength() << endl;
    cout << g->GetArea() << endl;
    delete g;
    g = nullptr;

    g = new Square(5);
    cout << g->GetLength() << endl;
    cout << g->GetArea() << endl;
    delete g;
    g = nullptr;

    g = new Circular(3);
    cout << g->GetLength() << endl;
    cout << g->GetArea() << endl;
    delete g;
    g = nullptr;

    //22
    //30
    //20
    //25
    //18.84
    //28.26

    #pragma endregion

}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏