C++系列-纯虚函数,抽象类,虚析构和纯虚析构

纯虚函数,抽象类,虚析构和纯虚析构


归园田居(其一)
陶渊明 魏晋

少无适俗韵,性本爱丘山。误落尘网中,一去三十年。

羁鸟恋旧林,池鱼思故渊。开荒南野际,守拙归园田。

方宅十余亩,草屋八九间。榆柳荫后檐,桃李罗堂前。

暧暧远人村,依依墟里烟。狗吠深巷中,鸡鸣桑树颠。

户庭无尘杂,虚室有余闲。久在樊笼里,复得返自然。


纯虚函数

在多态中,子类会重写父类中的虚函数,用于实现多态。通常父类中的虚函数是无意义的,一般不用。实际中主要是调用子类重写的内容,可以使用纯虚函数
纯虚函数:

  • virtual 返回类型 函数名(参数列表)=0, 这样的函数为纯虚函数。

抽象类

抽象类:当类中有了纯虚函数,则类称为抽象类。

抽象类的特点

  • 无法实例化对象。
  • 抽象类的子类必须重写抽象类中的纯虚函数,否则也属于抽象类(没重写的话,完全是继承父类的纯虚函数)

抽象类的举例

code: 
// 父类是抽象类,无法实例化对象, 子类Taro没有重写父类的纯虚函数,也是抽象类,无法实例化对象。
#include <iostream>
using namespace std;

class Vegetable
{
public:
	virtual void eat() = 0; //纯虚函数
};
class Spinach : public Vegetable
{
public:
	void eat()
	{
		cout << "多吃 菠菜 对身体好哦!" << endl;
	}
};

class Taro : public Vegetable
{
};

int main()
{
	// Vegetable veg1;		// 抽象类无法实例化对象
	Spinach sp1;			
	sp1.eat();		
	Vegetable* veg2 = new Spinach;	// 多态的使用
	veg2->eat();
	delete veg2;
	// Taro ta1;			// 抽象类无法实例化对象
	system("pause");
	return 0;
}

result:
多吃 菠菜 对身体好哦!
多吃 菠菜 对身体好哦!
#include <iostream>
using namespace std;

// 抽象父类,纯虚函数,不改变现有的其它结构,其它再有类似的饮料制作方法,直接后面再增加类,实现具体方法即可
class Beverage
{
public:
    virtual void boil() = 0 {}

    virtual void brew() = 0 {}

    virtual void add_additive() = 0 {}

    virtual void pour() = 0 {}
    
    void make_beverage()
    {
        boil();
        brew();
        add_additive();
        pour();
    }
};

class Tea : public Beverage
{
public:
    virtual void boil()
    {
        cout << "煮烧泠泠水" << endl;
    }
    virtual void brew()
    {
        cout << "冲泡瑟瑟尘" << endl;
    }
    virtual void add_additive()
    {
        cout << "添就茉莉香" << endl;
    }
    virtual void pour()
    {
        cout << "寄与爱茶人" << endl;
    }
};


class Coffee : public Beverage
{
public:
    virtual void boil()
    {
        cout << "煮自来水" << endl;
    }
    virtual void brew()
    {
        cout << "冲泡咖啡" << endl;
    }
    virtual void add_additive()
    {
        cout << "加植脂末等材料" << endl;
    }
    virtual void pour()
    {
        cout << "倒入杯中" << endl;
    }
};

int main()
{
    Beverage* bev1;      // 父类指针
    int choice;
    
    while (1)
    {
        cout << "------------ 请选择操作 ------------" << endl;
        cout << "1. 泡茶" << endl;
        cout << "2. 冲咖啡" << endl;
        cout << "0. 退出" << endl;
        cin >> choice;          //
        if (choice == 0)
        {
            cout << "退出过程" << endl;
            break;
        }
        else
        {
            switch (choice)
            {
            case 1:
                bev1 = new Tea();     // 父类指针指向子类对象
                break;
            case 2:
                bev1 = new Coffee();
                break;

            default:
                cout << "无效的选择" << endl;
                return 1;
            }
            bev1->make_beverage();
        }
        delete bev1;
    }
    system("pause");
    return 0;
}

虚析构

  • 多态应用中,如果子类中有属性开辟到堆区,那么父类指针或者引用在释放时无法调用到子类的析构代码(如果子类无析构函数),则会造成内存泄漏。
  • 将父类中的析构函数改为虚析构或者纯虚析构,在子类中实现自身的虚构函数,可以解决这个问题。
  • 需要有具体的函数实现(为了确保父类有析构代码执行)。
  • 如果子类中没有堆区数据,可以不写虚析构。

以下代码无法释放子类对象开辟在堆区的数据

code:
// 在delete父类指针指向的子类对象时,调用的是父类的析构函数,无法释放子类中开辟在堆区的数据
#include <iostream>
using namespace std;

class Vegetable
{
public:
	Vegetable()
	{
		cout << "Vegetable constructor" << endl;
	}
	virtual void eat() = 0; //纯虚函数
	~Vegetable()		
	{
		cout << "Vegetable destructor" << endl;
	}
};

class Spinach : public Vegetable
{
public:
	Spinach(string type)
	{
		cout << "Spinach constructor" << endl;
		m_type = new string(type);			// 创建在堆区
	}
	void eat()
	{
		cout << "Eating more " << * m_type << " spinach is good for health." << endl;
	}
	string *m_type;		
	
	~Spinach()
	{
		if (m_type != NULL)
		{
			cout << "Spinach destructor" << endl;
			delete m_type;
			m_type = NULL;
		}
	}
};

void test01()
{
	Vegetable* veg1 = new Spinach("organic");
	veg1->eat();
	// 父类指针在析构时候,不会调用子类中的析构函数,导致子类中如果有堆区数据,无法释放
	delete veg1;
}

int main()
{
	test01();
	system("pause");
	return 0;
}

result:
Vegetable constructor
Spinach constructor
Eating more organic spinach is good for health.
Vegetable destructor

将父类的析构函数改为虚析构

code:
// 在delete父类指针指向的子类对象时,先调用的是子类虚构函数,然后调用父类的
#include <iostream>
using namespace std;

class Vegetable
{
public:
	Vegetable()
	{
		cout << "Vegetable constructor" << endl;
	}
	virtual void eat() = 0; //纯虚函数
	virtual ~Vegetable()		
	{
		cout << "Vegetable destructor" << endl;
	}
};

class Spinach : public Vegetable
{
public:
	Spinach(string type)
	{
		cout << "Spinach constructor" << endl;
		m_type = new string(type);			// 创建在堆区
	}
	void eat()
	{
		cout << "Eating more " << * m_type << " spinach is good for health." << endl;
	}
	string *m_type;		
	
	~Spinach()
	{
		if (m_type != NULL)
		{
			cout << "Spinach destructor" << endl;
			delete m_type;
			m_type = NULL;
		}
	}
};

void test01()
{
	Vegetable* veg1 = new Spinach("organic");
	veg1->eat();
	// 父类指针在析构时候,先会调用子类中的析构函数,然后调用父类的析构函数
	delete veg1;
}

int main()
{
	test01();
	system("pause");
	return 0;
}

result:
Vegetable constructor
Spinach constructor
Eating more organic spinach is good for health.
Spinach destructor
Vegetable destructor

纯虚析构

虚析构和纯虚析构的特点:

  • 可以解决父类指针或引用释放子类对象时,子类对象的内存无法释放的问题。
  • 父类中的虚析构和纯虚析构需要有具体的函数实现。
  • 如果是纯虚函数,则类属于抽象类,无法实例化对象。
  • vitrual ~类名() {}
  • vitrual ~类名() = 0, 然后在类外实现, 类名:: ~类名() {}
  • 如果子类中没有堆区数据,可以不写虚析构。
code:
#include <iostream>
using namespace std;

class Vegetable
{
public:
	Vegetable()
	{
		cout << "Vegetable constructor" << endl;
	}
	virtual void eat() = 0; //纯虚函数
	virtual ~Vegetable() = 0;			// 纯虚析构函数需要有实现,因为如果有父类的一些数据需要释放,需要走析构函数的
};

Vegetable::~Vegetable()
{
	cout << "Vegetable pure destructor" << endl;
}


class Spinach : public Vegetable
{
public:
	Spinach(string type)
	{
		cout << "Spinach constructor" << endl;
		m_type = new string(type);			// 创建在堆区
	}
	void eat()
	{
		cout << "Eating more " << * m_type << " spinach is good for health." << endl;
	}
	string *m_type;		
	
	~Spinach()
	{
		if (m_type != NULL)
		{
			cout << "Spinach destructor" << endl;
			delete m_type;
			m_type = NULL;
		}
	}
};

void test01()
{
	Vegetable* veg1 = new Spinach("organic");
	veg1->eat();
	// 父类指针在析构时候,不会调用子类中的析构函数,导致子类中如果有堆区数据,无法释放
	delete veg1;
}

int main()
{
	test01();
	system("pause");
	return 0;
}

result:
Vegetable constructor
Spinach constructor
Eating more organic spinach is good for health.
Spinach destructor
Vegetable destructor
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值