归园田居(其一)
陶渊明 魏晋
少无适俗韵,性本爱丘山。误落尘网中,一去三十年。
羁鸟恋旧林,池鱼思故渊。开荒南野际,守拙归园田。
方宅十余亩,草屋八九间。榆柳荫后檐,桃李罗堂前。
暧暧远人村,依依墟里烟。狗吠深巷中,鸡鸣桑树颠。
户庭无尘杂,虚室有余闲。久在樊笼里,复得返自然。
纯虚函数
在多态中,子类会重写父类中的虚函数,用于实现多态。通常父类中的虚函数是无意义的,一般不用。实际中主要是调用子类重写的内容,可以使用纯虚函数。
纯虚函数:
- 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