侯捷老师 C++学习记录——Prototype

课程:C++面向对象高级编程(侯捷)
内容:设计模式中 Delegation + Inheritance 的 Prototype部分
问题:本人非计算机科班出身,基础较差。对该部分理解不够透彻,在此一步步 debug + 复现,深入理解。

根据侯捷老师的介绍,Prototype是一种设计比较巧妙的模式,简单来说:

  1. 子类通过自己的构造函数,把自己反挂回父类的数据中。
  2. 可以通过clone来找回子类。

根据课程,简单复现了一波代码:

1. 父类 水果类 ==> Fruit

需要注意的是:

  • 父类中的**_numTypes是子类的类型数目,而非实例数目**,这也是我一开始弄混的原因。
  • 对于addFruitType(),子类的 默认构造函数 会调用该函数,把子类实例添加进 _fruitTypes 中。具体来说,只有子类中 static member 实例在构造时才调用这个,后续 clone() 时调用的构造不再调用此函数。
class Fruit
{
    
    
private:
	static vector<Fruit*> _fruitTypes; //装载不同类型的子类(同一类型只装载一次)
	static int _numTypes;  //子类的类型数目,非实例的数量
protected:
	virtual fruitType returnType() const = 0; //返回子类的类型,用来返回对应类型的子类实例
	virtual Fruit* clone() = 0; //返回子类类型
	virtual void addFruitType(Fruit* fruit)
	{
    
    
		//子类的 默认构造函数 会调用该函数,把子类实例添加进_fruitTypes中
		//只有子类中 static member 实例在构造时才调用这个
		//后续clone时调用的构造不再调用此函数
		_numTypes++; 
		_fruitTypes.push_back(fruit);
	}
public:
	static Fruit* findAndClone(fruitType); //根据子类类型,返回子类实例,具体就是调用returnType()和clone()
	virtual void price() = 0;
};

int Fruit::_numTypes = 0;
vector<Fruit*> Fruit::_fruitTypes;

Fruit* Fruit::findAndClone(fruitType type)
{
    
    
	//根据子类类型,返回子类实例,具体就是调用returnType()和clone()
	for (int i = 0; i < _numTypes; i++)
	{
    
    
		if (_fruitTypes[i]->returnType() == type)
			return _fruitTypes[i]->clone();
			//根据类型返回对应的子类实例
	}
}

2. 子类 ==> AppleFruit, OrangeFruit

需要注意的是:

  • 子类中的**_count是子类实例数目,与父类不同,易弄混。
  • 子类的 默认构造函数 会调用 addFruitType() 函数,把子类实例添加进 _fruitTypes 中。
  • 子类的 默认构造函数 只调用一次,构造子类中的 static member 实例。
  • 子类的另外一个构造函数可以调用多次,每次返回一个子类的实例。但是其不能调用addFruitType(),因为在 static member 的构造函数中已经添加给父类数据了。
  • 子类的静态成员变量在main执行之前的静态初始化过程中分配内存并初始化,所以,此时会调用默认构造函数 ,其中再调用 addFruitType(this) 反挂回父类的数据中,实现 子类到父类反馈 的效果。

① AppleFruit

class AppleFruit : public Fruit
{
    
    
private:
	static AppleFruit _Apple;
	AppleFruit()
	{
    
    
		//只有static AppleFruit _Apple在构造时才调用时该函数
		//将_Apple反挂回父类的_fruitTypes中
		cout << "AppleFruit()" << endl;
		addFruitType(this);
	}
	int _id; //子类实例的编号
	static int _count; //子类实例的数量
protected:
	AppleFruit(int i)
	{
    
    
		//专门供clone()使用,返回对应类型的子类实例
		//不能调用addFruitType(),因为在static member的构造函数中已经添加给父类数据了
		cout << "AppleFruit(int i)" << endl;
		_id = _count++;
	}
public:
	void price()
	{
    
    
		cout << "Apple  => 1y" << endl;
	}
	fruitType returnType() const
	{
    
    
		return APPLE;
	}
	Fruit* clone()
	{
    
    
		//父类的findAndClone()会调用该函数
		//构造一个子类实例,返回
		return new AppleFruit(1);
	}
};

int AppleFruit::_count = 0;
AppleFruit AppleFruit::_Apple;
//类的静态成员变量在main执行之前的静态初始化过程中分配内存并初始化
//所以,此时会调用AppleFruit(),addFruitType(this)反挂回父类的数据中

② OrangeFruit

class OrangeFruit : public Fruit
{
    
    
private:
	static OrangeFruit _Orange;
	OrangeFruit()
	{
    
    
		//只有static OrangeFruit _Orange在构造时才调用时该函数
		//将_Orange反挂回父类的_fruitTypes中
		cout << "OrangeFruit()" << endl;
		addFruitType(this);
	}
	int _id; //子类实例的编号
	static int _count; //子类实例的数量
protected:
	OrangeFruit(int i)
	{
    
    
		//专门供clone()使用,返回对应类型的子类实例
		//不能调用addFruitType(),因为在static member的构造函数中已经添加给父类数据了
		cout << "OrangeFruit(int i)" << endl;
		_id = _count++;
	}
public:
	void price()
	{
    
    
		cout << "Orange => 2y" << endl;
	}
	fruitType returnType() const
	{
    
    
		return ORANGE;
	}
	Fruit* clone()
	{
    
    
		//父类的findAndClone()会调用该函数
		//构造一个子类实例,返回
		return new OrangeFruit(1);
	}
};

int OrangeFruit::_count = 0;
OrangeFruit OrangeFruit::_Orange;
//类的静态成员变量在main执行之前的静态初始化过程中分配内存并初始化
//所以,此时会调用OrangeFruit(),addFruitType(this)反挂回父类的数据中

3. 调用

const int NUM = 8; //子类实例的数量
fruitType input[NUM] =
{
    
     APPLE, APPLE, APPLE, ORANGE, APPLE, ORANGE, ORANGE, APPLE };
//每个子类实例对应的类型

int main()
{
    
    
	//在main()之前,子类的static member进行默认构造函数时,已经将子类实例反挂回了父类的_fruitTypes中
	cout << endl << "main() begin()" << endl << endl;
	Fruit* fruits[NUM];
	for (int i = 0; i < NUM; i++)
	{
    
    
		fruits[i] = Fruit::findAndClone(input[i]);
		//根据子类类型返回每个子类实例,内部调用的是 非默认构造函数
	}
	for (int i = 0; i < NUM; i++)
	{
    
    
		fruits[i]->price();
	}
	for (int i = 0; i < NUM; i++)
	{
    
    
		delete fruits[i];
	}
	system("pause");
	return 0;
}

3. 运行结果以及分析

在这里插入图片描述

  • 根据结果以及 debug 可以看到,子类的 static member 在 main() 之前初始化。所以,在初始化时就调用了
    默认构造函数,其中利用 addFruitType(this) 将子类实例反馈给父类。

  • 此后,进入main() 函数。

  • 调用 Fruit::findAndClone(fruitType)。其中,该函数查找 _fruitTypes(含子类实例),调用对应类型子类的 clone() 函数,来构造一个子类的实例并返回。后续的 8 个实例,都是通过 clone() 函数生成的。

  • 后续代码非常简单,不再分析。

猜你喜欢

转载自blog.csdn.net/weixin_37524256/article/details/109053229