类和对象03

news2024/11/14 19:54:12

六、继承

我们发现,定义这些类时,下级别的成员除了拥有上一级的共性,还有自己的特性。

这个时候我们就可以考虑利用继承的技术,减少重复代码

6.1 继承的基础语法

例如我们看到很多网站中, 都有公共的头部,公共的底部,甚至公共的左侧列表,只有中心内容不同接下来我们分别利用普通写法和继承的写法来实现网页中的内容,看一下继承存在的意义以及好处

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

//继承实现页面
//继承的好处:减少重复代码
//语法: class 子类 : 继承方式 父类
//子类  也称为  派生类
//父类  也称为  基类

class BasePage {
public:
	void header() {
		cout << "首页、公开课、登录、注册...(公共头部)" << endl;
	}

	void footer() {
		cout << "帮助中心、交流合作、站内地图...()" << endl;
	}
	void left() {
		cout << "Java、Python、C++...(公共分类列表)" << endl;
	}
};

//Java页面
class Java : public BasePage {
public:
	void content() {
		cout << "Java学科视频" << endl;
	}
};

//Python页面
class Python : public BasePage {
public:
	void content() {
		cout << "Python学科视频" << endl;
	}
};

//C++页面
class CPP : public BasePage {
public:
	void content() {
		cout << "CPP学科视频" << endl;
	}
};

void test01() {
	cout << "Java下载视频页面如下:" << endl;
	Java ja;
	ja.header();
	ja.footer();
	ja.left();
	ja.content();
	cout << "---------------" << endl;

	cout << "Python下载视频页面如下:" << endl;
	Python py;
	py.header();
	py.footer();
	py.left();
	py.content();
	cout << "---------------" << endl;

	cout << "C++下载视频页面如下:" << endl;
	CPP cpp;
	cpp.header();
	cpp.footer();
	cpp.left();
	cpp.content();


}


int main() {
	test01();

	system("pause");
	return 0;

}

总结:

继承的好处:可以减少重复的代码  class A : public B;

A类称为子类或派生类 。B类称为父类或基类

派生类中的成员,包含两大部分:

一类是从基类继承过来的,一类是自己增加的成员。从基类继承过过来的表现其共性,而新增的成员体现了其个性。

6.2 继承方式

继承的语法:

class 子类 : 继承方式 父类

 继承方式一共有三种:

公共继承   保护继承   私有继承

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

//公共继承
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;父类中私有访问权限 子类访问不到
	}
};

void test01() {
	Son1 s1;
	s1.m_A = 100;
	//s1.m_B = 100;//报错,到Son1中m_B为保护权限类外无法访问

}

//保护继承
class Base2 {
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son2 : private Base2 {
public:
	void func() {
		m_A = 10;//父类中的公共权限成员,到子类中变为了保护权限
		m_B = 10;//父类中的保护权限成员,到子类中依然是保护权限
		//m_C = 10;父类中私有访问权限 子类访问不到
	}
};

void test02() {
	Son2 s2;
	//s2.m_A = 100; //在Son2中m_A变为保护权限,因此类外访问不到
	//s2.m_B = 100; //在Son2中mB保护权限不可以访问

}

//私有继承
class Base3 {
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son3 : private Base3 {
public:
	void func() {
		m_A = 10;//父类中的公共权限成员,到子类中变为了私有权限
		m_B = 10;//父类中的保护权限成员,到子类中变为了私有权限
		//m_C = 10;父类中私有访问权限 子类访问不到
	}
};


void test03() {
	Son3 s3;
	//s3.m_A = 100; //在Son3中m_A变为私有权限,因此类外访问不到
	//s3.m_B = 100; //在Son2中m_B私有权限不可以访问

}
//
//class GrandSon3 : private Son3 {
//public:
//	void func() {
//		m_A = 100;//变为私有无法访问
//	}
//};

int main() {
	test01();
	test02();
	test03();

	system("pause");
	return 0;

}

6.3 继承中的对象类型

问题:从父类继承过来的成员,哪些属于子类对象中?

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

//继承中的对象模型
class Base {
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;
};

class Son : public Base {
public:
	int m_D;
};

void test01() {
	//16	
	//父类中所有非静态成员属性都会被子类继承下去
	//父类中私有成员属性是被编译器给隐藏了,因此是访问不到,但是确实被继承下去了
	cout << "size of son = " << sizeof(Son) << endl;
}

int main() {
	test01();

	system("pause");
	return 0;

}

利用Developer Command Prompt for vs 2022查看对象模型

跳转到盘符  D:

跳转文件路径cd具体路径下

查看命名
cl /d1 reportSingleClassLayout类名 文件名

6.4 继承中构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数

问题:父类和子类的构造和析构顺序是谁先谁后?

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

//继承中构造和析构的顺序
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;
}

int main() {
	test01();

	system("pause");
	return 0;

}

总结:继承中先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

6.5 继承同名成员的处理方式

问题:当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?

·访问子类同名成员,直接访问即可

 ·访问父类同名成员,需要加作用域

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

class Base {
public:
	Base() {
		m_A = 100;
	}
	void func() {
		cout << "Base - func() 调用" << endl;
	}
	void func(int a) {
		cout << "Base - func(int a) 调用" << 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 << "Son 下的 m_A = " << s.m_A << endl;
	//如果通过子类对象―访问到父类中同名成员,需要加作用域
	cout << "Base 下的 m_A = " << s.Base::m_A << endl;
}
//同名成员函数处理
void test02() {
	Son s;
	s.func();//直接调用调用是子类中的同名成员

	s.Base::func();
	//如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数
	//所以加作用域
	s.Base::func(100);

}

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

}

 总结:

1.子类对象可以直接访问到子类中同名成员

2.子类对象加作用域可以访问到父类同名成员

3.当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数

6.6 继承同名静态成员处理方式

问题:继承中同名的静态成员在子类对象上如何进行访问?

静态成员和非静态成员出现同名,处理方式一致

1.访问子类同名成员直接访问即可

2.访问父类同名成员需要加作用域

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

class Base {
public:
	static int m_A;
	static void func() {
		cout << "Base - static void func()" << endl;
	}
};

int Base::m_A = 100;

class Son : public Base {
public:
	static int m_A;
	static void func() {
		cout << "Son - static void func()" << endl;
	}
};
int Son::m_A = 200;

//同名静态成员属性
void test01() {
	Son s;
	//1.通过对象访问
	cout << "通过对象访问" << endl;
	cout << "Son 下的 m_A =  " << s.m_A << endl;
	cout << "Base 下的 m_A =  " << s.Base::m_A << endl;
	//2.通过类名访问
	cout << "通过类名访问" << endl;
	cout << "Son 下的 m_A =  " << Son::m_A << endl;
	//第一个::代表通过类名方式访问
	// 第二个::代表访问父类作用域下
	cout << "Base 下的 m_A =  " << Son::Base::m_A << endl;
}

//同名静态函数
void test02() {
	Son s;
	//1.通过对象访问
	cout << "通过对象访问" << endl;
	s.func();
	s.Base::func();
	//2.通过类名访问
	cout << "通过类名访问" << endl;
	Son::func();
	Son::Base::func();

}

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

}

总结:同名静态成员处理方式和非静态处理方式一样,只不过有两种访问的方式(通过对象和通过类名)

6.7 多继承语法

C++允许一个类继承多个类

语法:

class 子类∶继承方式 父类1,继承方式 父类2...

多继承可能会引发父类中有同名成员出现,需要加作用域区分

C++实际开发中不建议用多继承

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

class Base1 {
public:
	Base1() {
		m_A = 100;
	}

	int m_A;
};

class Base2 {
public:
	Base2() {
		m_A = 200;
	}

	int m_A;
};

//子类需要继承Base1和Base2
class Son : public Base1 ,public Base2{
public:
	Son() {
		m_C = 300;
		m_D = 400;
	}

	int m_C;
	int m_D;
};


void test01() {
	Son s;
	//16个
	cout << "sizeof Son = " << sizeof(Son) << endl;
	//当父类中出现同名成员,需要加作用域区分
	cout << "Base1 下的 m_A = " << s.Base1::m_A << endl;
	cout << "Base2 下的 m_A = " << s.Base2::m_A << endl;
}


int main() {
	test01();

	system("pause");
	return 0;

}

总结:多继承中如果父类中出现了同名情况,子类使用时候要加作用域

6.8 菱形继承

菱形继承概念:

两个派生类继承同一个基类    又有某个类同时继承者两个派生类   这种继承被称为菱形继承,或者钻石继承

eg.

菱形继承问题:

1.羊继承了动物的数据,范同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。

⒉草泥马继承自动物的数据继承了两份,其实我们应该清楚,这份数据我们只需要一份就可以。

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

class Animal {
public:
	int m_Age;
};

//利用虚继承解决菱形继承的问题
//继承之前加上关键字virtual变为虚继承
//Animal类称为虚基类
//羊类
class Sheep : virtual public Animal{};

//驼类
class Tuo : virtual public Animal{};

//羊驼类
class SheepTuo : public Sheep,public Tuo{};


void test01() {
	SheepTuo st;
	st.Sheep::m_Age = 18;
	st.Tuo::m_Age = 28;
	//当菱形继承,两个父类拥有相同数据,需要加以作用域区分
	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;
	//这份数据我们知道只有有一份就可以,菱形继承导致数据有两份,资源浪费
	
}


int main() {
	test01();

	system("pause");
	return 0;

}

 总结:

1.菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义

2.利用虚继承可以解决菱形继承问题

七、多态

7.1 多态的基本概念

多态是C++面向对象三大特性之一多态分为两类

1.静态多态:函数重载和这算符重载属于静态多态,复用函数名

2.动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态区别:

·静态多态的函数地址早绑定–编译阶段确定函数地址

·动态多态的函数地址晚绑定–运行阶段确定函数地址

下面通过案例进行讲解多态

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

class Animal {
public:
	//虚函数实现地址晚绑定
	virtual void speak() {
		cout << "动物在说话" << endl;
	}
};


class Cat :  public Animal {
public:
	//重写函数返回值类型函数名参数列表完全相同virtual这里写不写都行
	virtual void speak() {
		cout << "小猫在说话" << endl;
	}
};

class Dog : public Animal {
public:
	void speak() {
		cout << "小狗在说话" << endl;
	}
};


//执行说话的函数
//地址早绑定在编译阶段确定函数地址
//如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定,地址晚绑定

//动态多态满足条件
//1、有继承关系
//2、子类重写父类的虚函数


//动态多态使用
//父类的指针或者引用 指向子类对象

void doSpeak(Animal& animal) { //Animal &animal = cat;
	animal.speak();
}


void test01() {
	Cat cat;
	Dog dog;
	doSpeak(cat);
	doSpeak(dog);
}


int main() {
	test01();

	system("pause");
	return 0;

}

总结:

多态满足条件:

1.有继承关系

2.子类重写父类中的虚函数

多态使用条件:父类指针或引用指向子类对象

重写:函数返回值类型函数名参数列表完全—致称为重写

原理剖析:

7.2 多态案例一-计算器类

案例描述:

分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类

多态的优点:

1.代码组织结构清晰

2.可读性强

3.利于前期和后期的扩展以及维护

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

//普通写法
class Calculator {
public:
	int getResult(string oper) {
		if (oper == "+") {
			return m_Num1 + m_Num2;
		}
		else if (oper == "-") {
			return m_Num1 - m_Num2;
		}
		else if (oper == "*") {
			return m_Num1 * m_Num2;
		}
	}

	int m_Num1;
	int m_Num2;
};




void test01() {
	Calculator c;
	c.m_Num1 = 10;
	c.m_Num2 = 10;

	cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl;
	cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl;
	cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl;

	//如果想扩展新的功能,需求修改源码
	//在真实开发中提倡开闭原则
	//开闭原则:对扩展进行开发,对修改进行关闭
}


//利用多态实现计算器
//多态好处:
//1、组织结构清晰
//2、可读性强
//3、对于前期和后期扩展以及维护性高
class AbstractCaluator {
public:

	virtual int getResult() {
		return 0;
	}
	int m_Num1;
	int m_Num2;
};

//加法计算器类
class AddCalculator : public AbstractCaluator {
	int getResult() {
		return m_Num1 + m_Num2;
	}
};
//减法计算器类
class SubCalculator : public AbstractCaluator {
	int getResult() {
		return m_Num1 - m_Num2;
	}
};
//乘法计算器类
class MulCalculator : public AbstractCaluator {
	int getResult() {
		return m_Num1 * m_Num2;
	}
};

void test02() {
	//多态使用条件
	//父类指针或者引用指向子类对象

	//加法运算
	AbstractCaluator* abc = new AddCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;

	cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
	//用完后记得销毁
	delete abc;

	//减法运算
	abc = new SubCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;

	cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
	delete abc;

	//乘法运算
	abc = new MulCalculator;
	abc->m_Num1 = 10;
	abc->m_Num2 = 10;

	cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
	delete abc;
}

int main() {
	//test01();
	test02();

	system("pause");
	return 0;

}

总结:C++开发提倡利用多态设计程序架构,因为多态优点很多

7.3 纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容

因此可以将虚函数改为纯虚函数

纯虚函数语法:

virtual 返回值类型 函数名 (参数列表) =  0 ;

当类中有了纯虚函数,这个类也称为抽象类

抽象类特点:

1.无法实例化对象

2.子类必须重写抽象类中的纯虚函数,否则也属于抽象类

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

class Base {
public:
	//纯虚函数
	//只要有一个纯虚函数,这个类称为抽象类
	//抽象类特点:
	//1、无法实例化对象
	//2、抽象类的子类必须要重写父类中的纯虚函数,否则也属于抽象类
	virtual void func() = 0;
};

class Son : public Base {
public:
	virtual void func() {
		cout << "func函数调用" << endl;
	}
};

void test01() {
	/*Base b;无法实例化对象
	new Base;*/
	//Son s;//子类必须重写父类中的纯虚函数,否则无法实例化对象
	Base* base = new Son;
	base->func();

}

int main() {
	test01();

	system("pause");
	return 0;

}

7.4 多态案例二-制作饮品

案例描述:

制作饮品的大致流程为:煮水-冲泡–倒入杯中–加入辅料

利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

class AbstractDrinking {
public:
	//煮水
	virtual void Boil() = 0;

	//冲泡
	virtual void Brew() = 0;

	//倒入杯中
	virtual void PourInCup() = 0;

	//加入辅料
	virtual void PutSomeThing() = 0;

	//制作饮品
	void makeDrink() {
		Boil();
		Brew();
		PourInCup();
		PutSomeThing();
	}
};


//制作咖啡
class Coffee : public AbstractDrinking {
public:
	//煮水
	virtual void Boil() {
		cout << "煮农夫山泉" << endl;
	}

	//冲泡
	virtual void Brew() {
		cout << "冲泡咖啡" << endl;
	}

	//倒入杯中
	virtual void PourInCup() {
		cout << "倒入杯中" << endl;
	}

	//加入辅料
	virtual void PutSomeThing() {
		cout << "加入糖和牛奶" << endl;
	}
};

//制作茶叶
class Tea : public AbstractDrinking {
public:
	//煮水
	virtual void Boil() {
		cout << "煮矿泉水" << endl;
	}

	//冲泡
	virtual void Brew() {
		cout << "冲泡茶叶" << endl;
	}

	//倒入杯中
	virtual void PourInCup() {
		cout << "倒入杯中" << endl;
	}

	//加入辅料
	virtual void PutSomeThing() {
		cout << "加入枸杞" << endl;
	}
};

void doWork(AbstractDrinking* abs) {
	abs->makeDrink();
	delete abs;//释放
}

void test01() {
	//制作咖啡
	doWork(new Coffee);

	cout << "-----------" << endl;
	//制作茶叶
	doWork(new Tea);

}

int main() {
	test01();

	system("pause");
	return 0;

}

7.5 虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

 解决方式:将父类中的析构函数改为虚析构或者纯虚析构

虚析构和纯虚析构共性:

1.可以解决父类指针释放子类对象

2.都需要有具体的函数实现

虚析构和纯虚析构区别:如果是纯虚析构,该类属于抽象类,无法实例化对象

虚析构语法:

virtual ~类名(){}

纯虚析构语法:

virtual ~类名() = 0;

 类名::~类名(){}

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

class Animal {
public:
	Animal() {
		cout << "Animal构造函数调用" << endl;
	}
	利用虚析构可以解决父类指针释放子瑛对象时不干净的问题
	//virtual ~Animal() {
	//	cout << "Animal析构函数调用" << endl;
	//}
	
	//纯虚析构	需要声明也需要实现
	//有了纯虚析构之后,这个类也属于抽象类,无法实例化对象|
	virtual ~Animal() = 0;
	//纯虚函数
	virtual void speak() = 0;

};

Animal::~Animal() {
	cout << "Animal纯虚析构函数调用" << endl;
}

class Cat : public Animal {
public:
	Cat(string name) {
		cout << "Cat构造函数调用" << endl;
		m_Name = new string(name);
	}
	~Cat() {
		if (m_Name!= NULL) {
			cout << "Cat析构函数的调用" << endl;
			delete m_Name;
			m_Name = NULL;
		}
	}

	virtual void speak() {
		cout << *m_Name <<"小猫在说话" << endl;
	}
	string* m_Name;
};

void test01() {
	Animal* animal = new Cat("Tom");
	//父类指针在析构时候不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏
	animal->speak();
	delete animal;

}

int main() {
	test01();

	system("pause");
	return 0;

}

总结:

1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象

2.如果子类中没有堆区数据,,可以不写为虚析构或纯虚析构

3.拥有纯虚析构函数的类也属于抽象类

7.6 多态案例三-电脑组装

案例描述:

电脑主要组成部件为CPU(用于计算),显卡(用于显示),内存条(用于存储)

将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例Intel厂商和Lenovo厂商创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口

测试时组装三台不同的电脑进行工作

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string.h>
using namespace std;

//抽象不同零件类
//抽象CPU类
class CPU {
public:
	//抽象的计算函数
	virtual void calculate() = 0;
};

//抽象显卡类
class VideoCard {
public:
	//抽象的显示函数
	virtual void display() = 0;
};

//抽象内存条类
class Memory {
public:
	//抽象的存储函数
	virtual void storage() = 0;
};

//电脑类
class Computer {
public:
	Computer(CPU* cpu, VideoCard* vc, Memory* mem) {
		m_cpu = cpu;
		m_vc = vc;
		m_mem = mem;

	}
	~Computer() {
		if (m_cpu != NULL) {
			delete m_cpu;
			m_cpu = NULL;
		}
		if (m_vc != NULL) {
			delete m_vc;
			m_vc = NULL;
		}
		if (m_mem != NULL) {
			delete m_mem;
			m_mem = NULL;
		}
	}

	void work() {
		m_cpu->calculate();
		m_vc->display();
		m_mem->storage();
	}

private:
	CPU* m_cpu;//CPU零件指针
	VideoCard* m_vc;//显卡零件指针
	Memory* m_mem;//内存条零件指针

};

//具体厂商
//Intel
class IntelCPU : public CPU {
public:
	virtual void calculate() {
		cout << "Intel的CPU开始计算了!" << endl;
	}
};

class IntelVideoCard : public VideoCard {
public:
	virtual void display() {
		cout << "Intel的显卡开始显示了!" << endl;
	}
};

class IntelMemory : public Memory {
public:
	virtual void storage() {
		cout << "Intel的内存条开始存储了!" << endl;
	}
};

//Lenovo
class LenovoCPU : public CPU {
public:
	virtual void calculate() {
		cout << "Lenovo的CPU开始计算了!" << endl;
	}
};

class LenovoVideoCard : public VideoCard {
public:
	virtual void display() {
		cout << "Lenovo的显卡开始显示了!" << endl;
	}
};

class LenovoMemory : public Memory {
public:
	virtual void storage() {
		cout << "Lenovo的内存条开始存储了!" << endl;
	}
};

void test01() {
	CPU* intelCPU = new IntelCPU;
	VideoCard* intelCard = new IntelVideoCard;
	Memory* intelMem = new IntelMemory;

	cout << "第一台电脑开始工作" << endl;
	Computer* computer1 = new Computer(intelCPU, intelCard, intelMem);
	computer1->work();
	delete computer1;

	cout << "--------------------------" << endl;
	cout << "第二台电脑开始工作" << endl;
	Computer* computer2 = new Computer(new LenovoCPU,new LenovoVideoCard,new LenovoMemory);
	computer2->work();
	delete computer2;


	cout << "--------------------------" << endl;
	cout << "第三台电脑开始工作" << endl;
	Computer* computer3 = new Computer(new IntelCPU, new LenovoVideoCard, new IntelMemory);
	computer3->work();
	delete computer3;
}

int main() {
	test01();

	system("pause");
	return 0;

}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1701936.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据结构—二叉树相关概念【详解】【画图演示】

P. S.&#xff1a;以下代码均在VS2019环境下测试&#xff0c;不代表所有编译器均可通过。 P. S.&#xff1a;测试代码均未展示头文件stdio.h的声明&#xff0c;使用时请自行添加。 目录 1、二叉树的概念及结构1、1 二叉树的概念1、2 二叉树的结构 2、特殊的二叉树3、二叉树的性…

【Hive SQL 每日一题】行列转换

文章目录 行转列列传行 行转列 测试数据&#xff1a; DROP TABLE IF EXISTS student_scores;CREATE TABLE student_scores (student_id INT,subject STRING,score INT );INSERT INTO student_scores (student_id, subject, score) VALUES (1, Math, 85), (1, English, 78), (…

光纤跳线组成结构划分你知道吗

按照组成结构划分 光纤跳线根据组成结构的不同可分为带状光纤跳线和束状光纤跳线。带状光纤跳线使用的是由光纤带组成的带状光缆&#xff0c;大多呈扁平形状&#xff0c;因具有较高的光纤密度&#xff0c;它可以容纳更多的纤芯&#xff0c;因此大大节省布线成本和空间&#xf…

JavaScript 新特性:新增声明命令与解构赋值的强大功能

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; ES5、ES6介绍 文章目录 &#x1f4af;声明命令 let、const&#x1f35f;1 let声明符&a…

VMware虚拟机如何与主机共享文件夹

本机:WIN10 VMware虚拟机:WIN7 因为每次配置都爱忘记操作,目标是为了在WIN7虚拟机中可以访问本机文件 首先本机操作 新建一个共享文件夹,不带中文目录(最好不要) 点击共享 选择everyone,记得权限"读取和写入" 然后到虚拟机里面 添加一个网络位置 点击浏览,选择网…

刷题之从前序遍历与中序遍历序列构造二叉树(leetcode)

从前序遍历与中序遍历序列构造二叉树 前序遍历&#xff1a;中左右 中序遍历&#xff1a;左中右 前序遍历的第一个数必定为根节点&#xff0c;再到中序遍历中找到该数&#xff0c;数的左边是左子树&#xff0c;右边是右子树&#xff0c;进行递归即可。 #include<vector>…

ChatGPT-4o模型功能介绍——还没用过的看过来

1.概述 OpenAI 持续突破人工智能的边界&#xff0c;推出了其最新模型 ChatGPT-4o&#xff0c;作为 ChatGPT-4 的继承者&#xff0c;该模型有望带来显著的提升和创新功能。本文将深入解析 ChatGPT-4 与 ChatGPT-4o 之间的区别&#xff0c;并探讨它们的功能、性能以及潜在的应用…

5.22 R语言-正态性检验

正态性检验 正态性检验的目的是确定一组数据是否符合正态分布&#xff08;也称高斯分布&#xff09;。在统计分析和数据建模中&#xff0c;正态性假设是许多统计方法和模型的基础。了解数据是否符合正态分布有助于选择适当的统计方法和确保分析结果的有效性。 本文主要从概率…

神秘山洞惊现AI绘画至宝Stable Diffusion残卷

最近听到不少大宗门纷纷发声&#xff1a;随着AI神器的现世“程序员职业将不复存在”&#xff0c;“设计师将要失业”。 至此&#xff0c;不少修士开始担忧起来&#xff0c;现出世的AI神器会不会取代掉我辈修士。 其实&#xff0c;至女娲天神创造人类以来&#xff0c;在这漫漫…

RabbitMQ 交换机类型

常用交换机 发布订阅&#xff08;Publish/Subscribe&#xff09;交换机 一个生产者给多个队列发送消息&#xff0c;X 代表交换机。 交换机的作用&#xff1a;类似网络路由器&#xff0c;主要提供转发功能&#xff0c;解决怎么把消息转发到不同的队列中&#xff0c;让消费者从不…

基于Java+MySQL+Swing的学生管理系统

1.系统简介和开发背景 该同学工作积极主动、高效&#xff0c;学习认真&#xff0c;待人诚恳&#xff0c;能够做到服从指挥、认真听取老同志的指导&#xff0c;不怕苦、不怕累&#xff0c;表现有较强的求知欲&#xff0c;积极观察、体验、思考&#xff0c;并能够灵活运用自己的知…

Web安全技术期末考查-vulhub靶场搭建及漏洞复现

一、实验目的与要求 能根据报告找到难度适中的漏洞&#xff0c;搭建弱点环境&#xff0c;并验证该漏洞&#xff1b; 2.能给出该漏洞的修复建议。 二、实验原理与内容 漏洞原理 漏洞原理通常指的是计算机系统、软件、网络或其他技术系统中存在的安全缺陷&#xff0c;这些缺陷…

rapidssl泛域名https600元一年

泛域名https证书也可以称之为通配符https证书&#xff0c;指的是可以用一张https证书为多个网站(主域名以及主域名下的所有子域名网站)传输数据加密&#xff0c;并且提供身份认证服务的数字证书产品。RapidSSL旗下的泛域名https证书性价比高&#xff0c;申请速度快&#xff0c;…

浅谈JMeter体系结构

JMeter体系结构详解 JMeter是一款功能强大的开源性能测试工具&#xff0c;广泛应用于Web应用、数据库、FTP服务器等多种场景下的负载和压力测试。其灵活的体系结构设计使得测试计划的创建、执行与结果分析变得高效而直观。本文将深入解析JMeter的三维空间体系结构&#xff0c;…

SSE(Server Sent Event) 踩坑留念

整条链路是 客户端A --> 服务端 A —> 服务端 B 我负责服务端 A 此时要注意 Client 中的 processes 的写法 Post(value “/v2/xx”, processes MediaType.TEXT_EVENT_STREAM) 这样写是一直报错的 改成下面的写法才可以 Post(value “/v2/xx”, processes MediaT…

HTTP 协议的基本格式和Fidder的简单使用

HTTP协议诞生于1996&#xff08;开玩笑哈&#xff0c;诞生于1991年&#xff09;&#xff0c;http协议用于网页和手机app和服务器交互的场景。通过HTTP协议&#xff0c;客户端&#xff08;例如网页浏览器或手机应用&#xff09;可以向服务器发送请求&#xff0c;服务器则会响应这…

10个最佳人物素材网站推荐,免费获取第一个PNG文件!

人物素材是设计中应用最广泛的元素之一。无论是网页设计还是移动终端设计&#xff0c;人物素材的插画设计都比文字信息更容易吸引用户的注意力。作为内容呈现&#xff0c;还可以增加设计的艺术属性。为了节省大家寻找人物素材的时间成本&#xff0c;本文立即为大家整理了10个宝…

数据结构的直接插入排序(C语言版)

一.直接插入排序的基本概念 1.直接插入排序的基本思想 将数组分为已排序和未排序两部分。 每次从未排序部分取出一个元素,将其插入到已排序部分的合适位置,使得已排序部分保持有序。 重复步骤2,直到整个数组有序。 2.排序的工作原理 假设前 i-1 个元素已经有序,现在要将第…

【Django】开发个人博客系统【1】

使用Django开发个人博客系统&#xff0c;博客系统包括用户&#xff08;博主&#xff09;注册和登录、博主资料信息、图片墙功能、留言板功能、文章列表、文章正文内容和Admin后台系统。 1. 项目架构设计 下一步将上述设置写入Django的配置文件settings.py&#xff0c;当Django…

利用C#中WinForm设计一个小游戏:DoYouLoveMe???

目录 一、WinForm 二、功能实现 三、代码部分 一、WinForm Windows 窗体是用于生成 Windows 桌面应用的 UI 框架。 它提供了一种基于 Visual Studio 中提供的可视化设计器创建桌面应用的高效方法。 利用视觉对象控件的拖放放置等功能&#xff0c;可以轻松生成桌面应用。 使…