课程:C++面向对象高级编程(侯捷)
内容:设计模式中 Delegation + Inheritance 的 Prototype部分
问题:本人非计算机科班出身,基础较差。对该部分理解不够透彻,在此一步步 debug + 复现,深入理解。
根据侯捷老师的介绍,Prototype是一种设计比较巧妙的模式,简单来说:
- 子类通过自己的构造函数,把自己反挂回父类的数据中。
- 可以通过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() 函数生成的。
-
后续代码非常简单,不再分析。