C++面向对象
类中的属性和方法 & 类的实例化
#include <bits/stdc++.h>
using namespace std;
/*
类中的 属性和行为 统称为成员
属性i.e.成员属性、成员变量
行为i.e.成员方法、成员函数
*/
class Student //学生类
{
public: // 属性
string name;
int id;
public:
void Show_info() // 行为
{
cout << "学生姓名为:" << name << endl;
cout << "学号为:" << id << endl;
}
};
int main()
{
Student s1; // 实例化
s1.name = "xiaom";
s1.id = 123;
s1.Show_info();
return 0;
}
属性的访问权限
#include <bits/stdc++.h>
using namespace std;
/*
访问权限
种类 类内 类外
公共权限public 可访问 可访问
保护权限protected 可访问 不可访问(子类可访问父类中的保护内容)
隐私权限private 可访问 不可访问(子类不可访问父类中的私有内容)
*/
class Person //类
{
// 属性
public: // 公共属性
string Name;
protected: // 保护属性
string Car;
private: // 私有属性
string Password;
public:
void Show_info() // 类内访问
{
Name = "张三";
Car = "拖拉机";
Password = "123456";
}
};
int main()
{
Person p1; // 实例化
p1.name = "xiaom";
p1.Car = "BWM"; // 类外无法访问 protected属性
p1.Password = "123"; // 类外无法访问 private属性
return 0;
}
struct 和 class 的区别
- struct默认权限为共有;
- class 默认权限为私有;
成员属性设置为私有的好处
- 优点1:将所有成员属性设置为私有,可以自己控制读写权限
- 优点2:对于写权限,我们可以检测数据的有效性
#include <bits/stdc++.h>
using namespace std;
/*
访问权限
种类 类内 类外
公共权限public 可访问 可访问
保护权限protected 可访问 不可访问(子类可访问父类中的保护内容)
隐私权限private 可访问 不可访问(子类不可访问父类中的私有内容)
*/
class Person //类
{
public:
void SetName(string name)
{
Name = name;
}
void GetName()
{
cout << "The Name is " << Name << endl;
}
void GetAge()
{
cout << "The Age is " << Age << endl;
}
void SetIdol(string idol)
{
Idol = idol;
}
// 属性
private: // 私有属性
string Name; // read & write
int Age = 18; // Only read
string Idol; // Only write
};
int main()
{
Person p;
p.SetName("zhang");
p.GetName();
p.SetIdol("只因你太美");
p.GetAge();
return 0;
}
案例1:设计立方体类
设计立方体类cube
求出立方体面积和体积、分别用全局函数和成员函数判断两个立方体是否相等
#include <bits/stdc++.h>
using namespace std;
/*
访问权限
种类 类内 类外
公共权限public 可访问 可访问
保护权限protected 可访问 不可访问(子类可访问父类中的保护内容)
隐私权限private 可访问 不可访问(子类不可访问父类中的私有内容)
*/
class Cube
{
private:
int L, W, H;
public:
void Set_info(int l, int w, int h) // Set the length/weight/high
{
L = l;
W = w;
H = h;
}
int Get_L(){
return L;
}
int Get_W(){
return W;
}
int Get_H(){
return H;
}
int Get_v() // Get the volume
{
return L * W * H;
}
int Get_s() // Get the area
{
return 2*(L*W + L*H + W*H);
}
void Is_same(Cube &c) // Judge is same
{
if(c.Get_H() == H && c.Get_W() == W && c.Get_L() == L)
cout << "成员函数判断为同一个Cube\n";
else
cout << "成员函数判断不为同一个Cube\n";
}
};
//全局判断函数
void Is_same(Cube &c1, Cube &c2)
{
if(c1.Get_H() == c2.Get_H() && c1.Get_W() == c2.Get_W() && c1.Get_L() == c2.Get_L())
cout << "全局函数判断为同一个Cube\n";
else
cout << "全局函数判断不为同一个Cube\n";
}
int main()
{
Cube c1, c2;
c1.Set_info(10, 10, 10);
c2.Set_info(10, 10, 10);
//成员函数判断
c1.Is_same(c2);
//全局函数判断
Is_same(c1, c2);
return 0;
}
对象的初始化和清理
- 构造函数:初始化。在创建对象时为对象的成员属性赋值,由编译器自动调用,无需手动调用
- 析构函数:清理。在对象销毁前系统自动调用,执行清理工作。
构造函数语法:
1.没有返回值,不用写void
2.函数名与类名相同
3.构造函数可以有参数,可以发生重载
4.创建对象的时候,构造函数会自动调用,且只调用一次析构函数语法:
1.没有返回值,不用写void
2.函数名与类名相同,在名称前加~
3.构造函数 不 可以有参数,不可以发生重载
4.销毁对象前,析构函数会自动调用,且只调用一次
# include <bits/stdc++.h>
using namespace std;
/*
1.构造函数的分类与调用
按参数分类:无参构造 有参构造
按类型分类:拷贝构造 普通构造
调用:括号法、显示法、隐式转换法
*/
class Person
{
public:
Person()
{
cout << "无参构造函数调用,Person类创建!\n";
}
Person(int a)
{
age = a;
cout << "有参构造函数调用,Person类创建!\n";
}
// 拷贝构造函数
Person(const Person &p)
{
// 将p的所有属性拷贝到我身上
age = p.age;
cout << "拷贝函数调用,Person类创建!\n";
}
~Person()
{
cout << "析构函数调用,Person类销毁!\n";
}
public:
int age = 18;
};
int main()
{
// 1.括号法
Person p1; // 无参构造
Person p2(10); // 有参构造
Person p3(p2); // 拷贝构造函数
// cout << p2.age << ' ' << p3.age << endl;
// cout << (p3.age == p2.age) << endl;
// 2.显示法
Person(10); // 匿名对象。特点:系统链式创造的对象,系统会自动回收匿名函数
Person p4 = Person(10); // 有参构造
Person p5 = Person(p4); // 拷贝构造函数
//Person(p4) 不要用拷贝构造函数初始化匿名对象,编译器会认为 Person(p4) == Person p4,导致重定义p4
// 3.隐式转换
Person p6 = 10; // 编译器自动转换为 Person p6 = Person(10)
Person p7 = p6; // 隐式拷贝构造
return 0;
}
深拷贝&浅拷贝
- 深拷贝:简单的赋值拷贝操作
- 浅拷贝:在堆区重新申请空间,进行拷贝操作
类的属性也为类
- 构造:先构造属性对象,再构造自身
- 析构:先析构自身,再析构属性类
# include <bits/stdc++.h>
using namespace std;
/*
类对象作为类成员,先构造属性对象,再构造自身
*/
class Phone
{
public:
Phone(string name) : phonename(name)
{
cout << "Phone对象构造!\n";
}
~Phone()
{
cout << "Phone对象析构!\n";
}
public:
string phonename;
};
class Person
{
public:
Person(string name1, string name2) : personname(name1), p(name2) // 初始化列表传参
{
cout << "Person对象构造\n";
}
~Person()
{
cout << "Person对象析构!\n";
}
public:
string personname;
Phone p;
};
int main()
{
Person p("zhang", "Apple");
return 0;
}
静态成员(函数&变量)
静态成员变量:
1.在编译阶段分配内存
2.类内声明,类外初始化
3.所有对象共享共一份数据
静态成员函数:
1.所有对象共享同一个函数
2.静态成员函数只能访问静态成员变量
–调用静态成员函数方式:a.通过对象调用 b.通过类名调用
# include <bits/stdc++.h>
using namespace std;
/*
静态成员变量:
1.在编译阶段分配内存
2.类内声明,类外初始化
3.所有对象共享共一份数据
静态成员函数:
1.所有对象共享同一个函数
2.静态成员函数只能访问静态成员变量
--调用静态成员函数方式:a.通过对象调用 b.通过类名调用
*/
class Person
{
//类内声明静态变量
public:
static int m_a;
//静态成员也有访问权限
private:
static int m_b;
public:
static void func(){
m_a = 18; // 静态成员函数可以访问静态成员变量
cout << "func函数调用!\n";
}
private:
static void func2(){
cout << "func2函数调用!\n";
}
};
int Person::m_a = 100; // 类外初始化
int main()
{
Person p;
cout << p.m_a << endl; // 通过对象访问
cout << Person::m_a << endl; // 通过类名访问
// cout << Person::m_b << endl; // m_b是私有域,类外不可访问
Person p1;
p1.m_a = 200;
cout << p.m_a << endl; // 由于对象共享静态对象,故此时 p.m_a = 200
// 静态成员函数调用
p.func();
Person::func();
// Person::func2(); // 类外无法访问私有域静态函数
return 0;
}
c++对象模型和this指针
成员变量和成员函数分开存储
成员中只有非
静态变量存储在类上,其余静态函数、非静态函数、静态变量都不存储在类上。
空类占1B
this 指针
this指针是隐含每一个非静态成员函数内的一种指针。
当形参和成员变量同名时,可用this指针区分
非静态成员函数返回对象本身,可使用return *this
# include <bits/stdc++.h>
using namespace std;
class Person
{
public:
Person(int age)
{
this->age = age; // this指针处理成员变量与传参同名问题
}
Person& PersonAddAge(Person &p) // Person& 返回自身
{
this->age += p.age; //将这个类的年龄叠加p类的年龄
return *this; // 将这个类返回
}
int age;
};
int main()
{
Person P1(10);
Person P2(10);
P2.PersonAddAge(P1).PersonAddAge(P1); // 每次返回都为一个Person类,调用2次,实现 2*(+10)
cout << P2.age << endl;
return 0;
}
常对象(const)& 常函数(func()const)
/*常函数:*/
class Person
{
public:
Person()
{
this->age = 18;
}
//this指针本质是指针常量,指针指向不可修改
// 成员函数后面加const变为常函数,修饰的是this指针 i.e. const Person * const this
void showPerson() const // 加const后,此时this指针不但不可以修改this指向类的属性也不可修改
{
// this->age = 100; // 报错
this->B = 100; // 不报错
}
void func(){}
int age;
mutable int B; // 加mutable,特殊变量,即使在常函数中也可修改
};
//常对象, 只能修改常变量和调用常函数
void test01()
{
const Person p;
// p.age = 10; // 报错
p.B = 10; // 不报错
// p.func(); // 报错,常对象只能调用常函数不能调用普通函数
}
友元
友元friend
目的是让一个函数or类访问另一个类中的私有privte成员
三种实现:
全局函数友元
类做友元
* 成员函数做友元
# include <bits/stdc++.h>
using namespace std;
/*全局函数做友元:*/
class Building
{
// 告诉编译器 全局函数Goodgay 是Building类的好朋友,可以访问类中的私有内容
friend void Goodgay(Building& p); // 没有这行声明,L25将会报错
public:
Building()
{
this->bed_room = "卧室";
this->setting_room = "客厅";
}
string setting_room;
private:
string bed_room;
};
void Goodgay(Building& p)
{
cout << "Goodgay正在访问:" << p.setting_room << endl;
cout << "Goodgay正在访问:" << p.bed_room << endl;
}
int main()
{
Building building;
Goodgay(building);
return 0;
}
运算符重载
加号+
重载
# include <bits/stdc++.h>
using namespace std;
/* + 运算符重载*/
class Person
{
public:
// 1.成员函数重载+
Person operator+(Person &p)
{
Person temp;
temp.m_A = this->m_A + p.m_A;
temp.m_B = this->m_B + p.m_B;
return temp;
}
int m_A, m_B;
};
// 2.全局函数重载+
Person operator+(Person &p1, Person &p2)
{
Person temp;
temp.m_A = p1.m_A + p2.m_A;
temp.m_B = p1.m_B + p2.m_B;
return temp;
}
// 2.1全局函数重载+并进行函数重载
Person operator+(Person &p1, int num)
{
Person temp;
temp.m_A = p1.m_A + num;
temp.m_B = p1.m_B + num;
return temp;
}
void test01()
{
Person p1, p2;
p1.m_A = p1.m_B = 10;
p2.m_A = p2.m_B = 10;
Person p3, p4;
p3 = p1.operator+(p2); // 成员函数重载调用本质
p3 = operator+(p1, p2); //全局函数重载调用本质
p3 = p1 + p2; // 简化版本,编译器同样可以处理
cout << p3.m_A << ' ' << p3.m_B << endl;
// 运算符重载也可以实现函数重载
p4 = p1 + 10;
cout << p4.m_A << ' ' << p4.m_B << endl;
}
int main()
{
test01();
return 0;
}
左移<<
重载
/* << 运算符重载 */
class Person
{
public:
int m_A, m_B;
};
//只能利用全局函数重载 <<
ostream & operator<<(ostream& cout, Person &p) //opeartor<<(cout, p) 简化为cout<<p
{
cout << "m_A = " << p.m_A << " m_B = " << p.m_B;
return cout; // 返回cout是为了实现链式函数,每次调都返回cout那么就可以cout多个内容cout << a << b.
}
void test01()
{
Person p1;
p1.m_A = p1.m_B = 10;
cout << p1 << endl;
}
递增++
重载
# include <bits/stdc++.h>
using namespace std;
/* ++ 运算符重载 */
class MyInteger
{
friend ostream& operator<<(ostream& cout, MyInteger myint);
public:
MyInteger()
{
m_num = 0;
}
//前置++,返回引用是为了一直对一个数据进行递增操作
MyInteger& operator++()
{
m_num ++; // 先++
return *this; // 返回本身
}
//后置++ int代表占位符,可以用于区分前置和后置递增
//后置不能返回引用,temp是临时变量,如果返回temp,函数运行完temp就释放了,再调用返回的temp即为非法
MyInteger operator++(int)
{
// 先记录目前结果
MyInteger temp = *this;
// 在进行递增更新
this->m_num++;
return temp;
}
private:
int m_num;
};
// 本质 operator<<(cout, p),简化 cout<<p.
// cout是别名,可以去out,kn,...
// cout 是ostream输出流数据类型
ostream& operator<<(ostream& cout, MyInteger myint)
{
cout << myint.m_num;
return cout;
}
void test01()
{
MyInteger myint;
cout << ++(++myint) << endl;
cout << myint << endl;
}
void test02()
{
MyInteger myint;
cout << myint++ << endl;
cout << myint << endl;
}
int main()
{
test01();
test02();
return 0;
}
赋值=
重载
为了解决内置赋值=
的浅拷贝导致的问题,而重载=
/* = 运算符重载 */
class Person
{
public:
Person(int age)
{
m_age = new int(age); // 开辟一块内存内容为age,返回该内存指针给m_age
}
Person& operator=(Person &p)
{
/*
//编译器提供浅拷贝:m_age = p.m_age;
p2=p1此时是浅拷贝,都指向p1.age的地址,
其中一个结束后直接执行析构函数将指向地址的内存释放,
另一个仍指向那个地址析构的时候会再次释放已经释放过的那个内存从而报错
*/
//将原来的地址释放干净在进行深拷贝
if(m_age != NULL)
{
delete m_age;
m_age = NULL;
}
//深拷贝,再申请一块新内存,放*p1.age的值,将p2.age指向这块新的内存
m_age = new int(*p.m_age);
return *this;
}
~Person()
{
if(m_age != NULL)
{
delete m_age;
}
m_age = NULL;
}
int *m_age;
};
void test01()
{
Person p1(18);
Person p2(20), p3(11);
p3 = p2 = p1;
cout << *p1.m_age << endl;
cout << *p2.m_age << endl;
cout << *p3.m_age << endl;
}
关系运算符<, >, ==, !=
重载
/* ==运算符重载 */
class Person
{
public:
Person(int age, string name)
{
m_age = age;
m_name = name;
}
bool operator==(Person& p)
{
return (m_age == p.m_age && m_name == p.m_name);
}
string m_name;
int m_age;
};
void test01()
{
Person p1(18, "xiao");
Person p2(18, "xiao");
if(p1 == p2)
{
cout << "p1 == p2" << endl;
}
else cout << "p1 != p2" << endl;
}
函数调用运算符重载
- 函数调用yu运算符
()
也可以重载 - 由于重载后非常像函数的调用,因此称为仿函数
- 仿函数没有固定的写法,非常之灵活
/* 函数调用运算符重载 */
class MyPrint
{
public:
void operator() (string test)
{
cout << test << endl;
}
};
class MyAdd
{
public:
int operator() (int num1, int num2){
return num1 + num2;
}
};
void test01()
{
MyPrint myprint;
myprint("hello world"); // 由于使用起来非常想函数调用,因此成为仿函数
}
void test02()
{
MyAdd myadd;
cout << myadd(10, 10) << endl;
// 匿名函数对象
cout << MyAdd() (100, 100) << endl; // 匿名对象MyAdd(),临时创建
}
继承
继承可以减少重复代码;
语法: class 子类/派生类 : 继承方式 父类/基类
. eg.class A : public B
/* 继承 */
class Basepage
{
public:
void header()
{
cout << "头部" << endl;
}
void footer()
{
cout << "底部" << endl;
}
void left()
{
cout << "左部分类" << endl;
}
};
//继承
class Java : public Basepage
{
public:
void content() // 自己与父类不同的内容
{
cout << "Java学科视频" << endl;
}
};
class CPP : public Basepage
{
public:
void content()
{
cout << "CPP学科视频" << endl;
}
};
void test01()
{
CPP cpp;
cpp.header();
cpp.content();
cpp.footer();
cpp.left();
};
继承方式
- 公共继承
- 保护继承
- 私有继承
/* 继承方式 */
class Base1
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
//公共继承
class Son1 : public Base1
{
public:
void func()
{
m_A = 10; // 父类公共权限成员,到子类是公共权限
m_B = 10; // 父类保护权限成员,到子类是保护权限
// m_C = 10; // 父类私有权限成员,子类无法访问
}
};
// 保护继承
class Son2 : protected Base1
{
public:
void func()
{
m_A = 10; // 父类公共权限成员,到子类是保护权限
m_B = 10; // 父类保护权限成员,到子类是保护权限
// m_C = 10; // 父类私有权限成员,子类无法访问
}
};
// 私有继承
class Son3 : private Base1
{
public:
void func()
{
m_A = 10; // 父类公共权限成员,到子类是私有权限
m_B = 10; // 父类保护权限成员,到子类是私有权限
// m_C = 10; // 父类私有权限成员,子类无法访问
}
};
void test01()
{
Son1 s1;
s1.m_A = 100;
// s1.m_B = 100; // Son1公共继承Base1后,保护权限无法类外访问
};
继承中的对象模型
- 父类中的所有成员均被子类继承,其中的私有成员会在子类中隐藏从而访问不到父类的私有成员
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C; // 父类私有成员不能被子类继承(实际上是被子类继承但隐藏了)
};
//公共继承
class Son : public Base
{
public:
int m_D;
};
void test01()
{
cout << "size of Son = " << sizeof(Son) << endl; // 16 Byte
};
继承中构造和析构顺序
子类继承父类后,当创建子类对象,也会调用父类的构造函数
问题:父类和子类中的构造和析构顺序是谁先谁后?
输出:
Base构造函数
Son构造函数
Son析构函数
Base析构函数
class Base
{
public:
Base()
{
cout << "Base构造函数" << endl;
}
~Base()
{
cout << "Base析构函数" << endl;
}
};
//公共继承
class Son : public Base
{
public:
Son()
{
cout << "Son构造函数" << endl;
}
~Son()
{
cout << "Son析构函数" << endl;
}
};
void test01()
{
Son s;
};
继承同名成员处理方式
Q:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
A:
* 访问子类同名成员,直接访问即可
* 访问父类同名成员,需要加作用域
class Base
{
public:
Base()
{
m_A = 100;
}
void func(int a)
{
cout << "Base的func被调用!" << endl;
}
int m_A;
};
//公共继承
class Son : public Base
{
public:
Son()
{
m_A = 200;
}
void func()
{
cout << "Son的func被调用!" << endl;
}
int m_A;
};
// 同名属性
void test01()
{
Son s;
cout << "m_A = " << s.m_A << endl; // Son 下的m_A
cout << "m_A = " << s.Base::m_A << endl; // Base 下的m_A
// 即使参数不同(子类重载父类同名函数)编译器也不知道访问的那个,只要同名就要加作用域才能调用父类函数。
s.func(); // Son 下的同名函数调用
s.Base::func(100); // Base 下的同名函数调用
};
继承同名静态成员
class Base
{
public:
void func(int a)
{
cout << "Base的func被调用!" << endl;
}
static void func()
{
cout << "Base static void func() 调用" << endl;
}
public:
static int m_A;
};
int Base::m_A = 100; // 类内静态成员,类外初始化
//公共继承
class Son : public Base
{
public:
static void func()
{
cout << "Son static void func() 调用" << endl;
}
static int m_A;
};
int Son::m_A = 200;
// 同名静态属性
void test01()
{
// 1.通过对象访问
cout << "通过对象访问" << endl;
Son s;
cout << "m_A = " << s.m_A << endl; // Son 下的m_A
cout << "m_A = " << s.Base::m_A << endl; // Base 下的m_A
// 2.通过类名访问
cout << "通过类名访问" << endl;
cout << "Son`s m_A = " << Son::m_A << endl;
//第一个::代表通过类名访问;第二个::代表访问父类作用域下
cout << "Base`s m_A = " << Son::Base::m_A << endl;
};
//同名静态函数
void test02()
{
// 1.通过对象访问
Son s;
s.func();
s.Base::func();
// 2.通过类名调用
Son::func();
Son::Base::func();
}
多继承语法
C 允许一个类继承多个类(一个儿子认多个叠)
语法:Class A : public Base1, protected Base2, private Base3 ...
多继承可能会引发父类中有同名成员出现,需要加作用域区分
**C实际开发中不建议使用多继承**
class Base1
{
public:
Base1()
{
m_A = 100;
}
public:
int m_A;
};
class Base2
{
public:
Base2()
{
m_A = 200;
}
public:
int m_A;
};
//多继承
class Son : public Base1, public Base2
{
public:
Son()
{
m_C = 300;
m_D = 400;
}
int m_C, m_D;
};
void test01()
{
Son s;
cout << "size of Son = " << sizeof(Son) << endl;
cout << "Base1`s m_A = " << s.Base1::m_A << endl;
cout << "Base2`s m_A = " << s.Base2::m_A << endl;
};
菱形继承
- 利用虚继承解决菱形继承问题
- 语法:
class B : virtual public A
//动物类
class Animal
{
public:
int m_Age;
};
//利用虚继承 解决菱形继承的问题
//羊类
class Sheep : virtual public Animal
{
};
//驼类
class Tuo : virtual public Animal
{
};
//羊驼类
class SheepTuo : public Sheep, public Tuo
{};
void test01()
{
SheepTuo ST;
ST.Sheep::m_Age = 20;
ST.Tuo::m_Age = 30;
cout << "ST.Sheep::m_Age = " << ST.Sheep::m_Age << endl;
cout << "ST.Tuo::m_Age = " << ST.Tuo::m_Age << endl;
cout << "ST.m_Age = " << ST.m_Age << endl;
};
多态
多态分为两类:
静态多态:函数重载 和 运算符重载 属于静态多态,复用函数名
动态多态:派生类和虚函数实现运行时多态
静态和动态多态区别:
静态多态的函数地址早绑定-编译阶段确定函数地址
动态多态的函数地址晚绑定-运行阶段确定函数地址
//动态多态满足条件
// 1.有继承关系
// 2.子类要重写父类的虚函数
//动物类
class Animal
{
public:
virtual void speak() // 虚函数。此时可以实现晚绑定
{
cout << "动物在说话" << endl;
}
};
// 猫类
class Cat : public Animal
{
public:
void speak()
{
cout << "小猫在说话" << endl;
}
};
// 执行说话的函数.
//这里是地址早绑定,编译阶段就确定函数地址。不管传入的类型是什么,传入后都为Animal类型
// 如果想执行“小猫在说话”,那么这个函数地址不能提前绑定,需要在运行阶段进行绑定即地址万绑定
//动态多态满足条件
// 1.有继承关系
// 2.子类要重写父类的虚函数
void DoSpeak(Animal &animal)
{
animal.speak();
}
void test01()
{
Cat cat;
DoSpeak(cat);
};
纯虚函数&抽象类
在多态中,父类的虚函数通常是无意义的,主要调用的都是子类重写的内容
因此可将父类的虚函数改为纯虚函数
(virtual 返回值类型 func(参数) = 0
)
当类中有了纯虚函数,那么这个类称为抽象类。
抽象类特点:
无法实例化对象
子类必须重写抽象类中的纯虚函数,否则也属于抽象类
虚析构&纯虚析构
多态使用时,若子类中有属性开辟在堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构
或纯虚析构
虚析构&纯虚析构:
可以解决父类指针释放子类对象
都需要有具体函数实现
* 若为纯虚析构,该类属于抽象类,无法实例化对象
虚析构:virtual ~类名(){}
纯虚析构:virtual ~类名() = 0
案例:电脑组装
电脑主要组成部件为:CPU、显卡、内存条
将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商
创建电脑类提供让电脑工作的函数,并且调用每个零件的接口
测试时组装三台不同电脑进行工作
# include <bits/stdc++.h>
using namespace std;
// CPU抽象类
class Cpu
{
public:
virtual void Calculate() = 0;
};
// GraphCard抽象类
class GraphCard
{
public:
virtual void Display() = 0;
};
// Memory抽象类
class Memory
{
public:
virtual void Storage() = 0;
};
class Computer
{
public:
Computer(Cpu* cpu, GraphCard* graphcard, Memory* memory)
{
m_cpu = cpu;
m_graphcard = graphcard;
m_memory = memory;
}
void work() // 电脑工作
{
m_cpu->Calculate();
m_graphcard->Display();
m_memory->Storage();
}
~Computer()
{
if(m_cpu != NULL)
{
delete m_cpu;
m_cpu = NULL;
}
if(m_graphcard != NULL)
{
delete m_graphcard;
m_graphcard = NULL;
}
if(m_memory != NULL)
{
delete m_memory;
m_memory = NULL;
}
}
private: // 各部件指针
Cpu* m_cpu;
GraphCard* m_graphcard;
Memory* m_memory;
};
//Intel部件
class IntelCpu : public Cpu
{
public:
virtual void Calculate(){
cout << "Intel Cpu 开始计算!\n";
}
};
class IntelGraphCard : public GraphCard
{
public:
virtual void Display(){
cout << "Intel GraphCard 开始显示!\n";
}
};
class IntelMemory : public Memory
{
public:
virtual void Storage(){
cout << "Intel Memory 开始存储!\n";
}
};
//Lenovo部件
class LenovoCpu : public Cpu
{
public:
virtual void Calculate(){
cout << "Lenovo Cpu 开始计算!\n";
}
};
class LenovoGraphCard : public GraphCard
{
public:
virtual void Display(){
cout << "Lenovo GraphCard 开始显示!\n";
}
};
class LenovoMemory : public Memory
{
public:
virtual void Storage(){
cout << "Lenovo Memory 开始存储!\n";
}
};
void test01()
{
Cpu* intelCPU = new IntelCpu;
Memory* intelMemory = new IntelMemory;
GraphCard* intelgraphcard = new IntelGraphCard;
cout << "第一台电脑开始工作:\n";
Computer* computer = new Computer(intelCPU, intelgraphcard, intelMemory);
computer->work();
delete computer;
cout << "----------------------------\n";
cout << "第二台电脑开始工作:\n";
Computer* computer2 = new Computer(new LenovoCpu, new LenovoGraphCard, new LenovoMemory);
computer2->work();
delete computer2;
cout << "----------------------------\n";
cout << "第三台电脑开始工作:\n";
Computer* computer3 = new Computer(new LenovoCpu, new LenovoGraphCard, new IntelMemory);
computer3->work();
delete computer3;
}
int main()
{
test01();
return 0;
}
第一台电脑开始工作:
Intel Cpu 开始计算!
Intel GraphCard 开始显示!
Intel Memory 开始存储!
----------------------------
第二台电脑开始工作:
Lenovo Cpu 开始计算!
Lenovo GraphCard 开始显示!
Lenovo Memory 开始存储!
----------------------------
第三台电脑开始工作:
Lenovo Cpu 开始计算!
Lenovo GraphCard 开始显示!
Intel Memory 开始存储!