女娲造人
----构造析构与对象的生死
作者:HolyFire
在《由始至终----构造与析构》最后我提到了C++中的构造函数和析构函数与对象的生和死有着很大的关系,好好运用还能控制对象的生死,使得编程的思路更接近现实世界的规律。
我们先回忆一下,在对象创建的时候,合适的构造函数将被自动调用,而对象销毁的时候,析构函数将被自动调用。在构造函数与析构函数可以被随意调用的情况下,也就是被声明为public的时候,这没有任何问题,但是一旦我们将他们的可视性改为private与protected呢,那么情况就有所改观了。
声明为private的时候,那么除了类本身可以调用外,还有用friend关键字赋予信任关系的对象(一般是类或者函数)可以访问。
#include <iostream>
using namespace std;
class A{
public:
static A * CreateA( void )
{
cout << "Create a A instance By class A's Member Function" << endl;
return new A;
}
private:
A(){ }
friend A * CreateA( void );
};
A * CreateA( void )
{
cout << "Create a A instance By class A's friend Function" << endl;
return new A;
}
void main()
{
A * a1 = A::CreateA();
A * a2 = CreateA();
delete a1;
delete a2;
cin.get();
}
结果是:
Create a A instance By class A's Member Function
Create a A instance By class A's friend Function
如果你直接用A a这样的方式,是无法创建A的实例的,编译器会告诉你,它帮不上忙。
当然,将析构函数声明为私有也可以限制将A的实例销毁的条件,那么
delete a1;
delete a2;
也会遭到编译器的抗议,我们需要另外写一个函数或者类来销毁他们。
在现实生活中有很多事物是有限的。比如说太阳系中只有一个太阳,如果有两个太阳会出什么乱子呢,真的是难以想象。在创建的对象有条件限制的时候,我们可以写文档告诉别人在编程的时候注意,但是随着工程的日益庞大,不要说别人,连自己也有可能忘记,在对条件限制要求非常严格的情况下,我们的知识便派上了用场。
结合刚才讲的将构造函数声明为私有的内容和《独一无二----静态成员变量》的内容,我们可以设计一个只能有一个实例的类型。下面是C++的源代码,这个例子中,对实例的销毁也加上了限制。
#include <iostream>
using namespace std;
class A{
private:
static A * a; //一个静态成员变量,类的所有实例所共有的
A(){ cout << "This is class A , I couldn't direct Creat instance" << endl; }
~A(){ cout << "This is Class A , I coudn't direct Destroy instance" << endl; }
public:
void ShowInstance(){ cout << this << endl; }
friend A * CreatObjectOfA( void ); //委托信任关系,由于类没有提供创建实例的方法,那么这个任务就交给了这个函数了。
friend void DestroyObjectOfA( A * a ); //对应创建,这个函数负责销毁产生的实例
};
A* A::a = NULL;
A * CreatObjectOfA( void )
{
if( A::a == NULL ) //之前有没有创建过A的实例
{//没有
A::a = new A;
cout << "This is a Class A Instance Creat By CreatObjectOfA Function ." << endl;
}//创建过的话就不用再创建了
return A::a; //由于A::a是静态成员变量,那么返回的都是同一个指针
}
void DestroyObjectOfA( A * _a )
{
if( A::a != NULL )//对应创建,如果现在有实例被创建过,那么销毁它
{
delete A::a;
A::a = NULL;
cout << "This is a Class A Instance Destroy By DestroyObjectOfA Function ." << endl;
}
_a = NULL;
}
void main()
{
A * a1 , * a2 , * a3;
a1 = CreatObjectOfA();
a2 = CreatObjectOfA();
a3 = CreatObjectOfA();
a1->ShowInstance();
a2->ShowInstance();
a3->ShowInstance();
DestroyObjectOfA( a1 );
DestroyObjectOfA( a2 );
DestroyObjectOfA( a3 );
cin.get();
}
结果是:
This is class A , I couldn't direct Creat instance
This is a Class A Instance Creat By CreatObjectOfA Function .
00864898 //指向的都是同一个地方
00864898
00864898
This is Class A , I coudn't direct Destroy instance
This is a Class A Instance Destroy By DestroyObjectOfA Function .
至始至终只有一个实例被创建和销毁了
无论你创建多少次,这个类的实例最多有一个,这正是我们想要的。只要你肯去想,有很多有趣的事物在等着你。
将构造函数与析构函数声明为protected(保护),那么我们的目的就是这个类将作为基类,而且这个基类不能直接实例化。为什么要这样做,事实上我们在理解事物和对大量事物进行抽象的时候,会有一些不完整的片断,他们单独的出现是不合适的。比如说一些简单加工后的半成品,甚至是现实中找不到例子的,他们的语义是残缺不全的。也有完全抽象化的,比如说:“人”,不具体到男人,女人,小孩,大人,就无法用现实的思想来考虑它。
那么就用一个C++的例子来实现一下,从而掌握它。
#include <iostream>
using namespace std;
class A{
protected:
A(){ cout << "This is class A , I couldn't direct Creat instance" << endl;}
};
class B : public A{
public:
B(){ cout << "This is class B public inherit From class A , I could Creat instance By class B" << endl; }
};
void main()
{
B b;
cin.get();
}
结果是:
This is class A , I couldn't direct Creat instance
This is class B public inherit From class A , I could Creat instance By class B
这个例子很简单,我们不需要花多少时间就能理解。
到现在才回到标题上来似乎有些突然,但是没有上面的解释,理解起来会有些困难,那么废话少说,我们直接切入主题。
中国古老的传说中,人是女娲用泥巴做的,所以说,人不能随意创建,可以创建人的事物可以是女娲。
女娲变出了男人和女人,女人可以生孩子,也就是可以创建人,但是只有男人才能让女人生孩子,所以关系是,要创建人必须要有一个男人来让一个女人生孩子,生孩子的行为是女人的,但是主动权也就是男人才有权利使用女人的行为,这是男人的行为。我们可以理解为男人的行为是发一条消息来告诉女人,女人采用相应的行为来处理。
我们来整理一下:(我使用一种简化的模式来考虑问题,要点在于理清构造函数对于对象生死的关系)
男人和女人都是人,女人可是生孩子,孩子也是人,所以孩子要分成男人和女人,那么女人生孩子事实上是两种行为,生男孩,生女孩,男人也有两种行为,让女人生男孩,让女人生女孩。
女娲{ 创造男人,创造女人 }
人{}
男人{ 人 ,让女人生男孩(男人) ,让女人生女孩(女人)}
女人{ 人 ,生男孩(男人),生女孩(女人)}
class Person{ //人
protected:
Person(){} //构造函数可以让派生类使用,也就是男人和女人使用
};
class Man;
class Woman;
class NvWa{ //女娲
public:
Man * CreatAMan(void); //创造男人
Woman * CreatAWoman( void ); //创造女人
};
class Woman : virtual public Person{ //女人
private:
Woman(){}
Man * CreatAMan( void ); //生男孩(男人)
Woman * CreatAWoman( void ); //生女孩(女人)
friend NvWa;
friend Man;
};
class Man:virtual public Person{ //男人
private:
Man(){}
public:
Man * GetABoy( Woman * woman ) //让女人生男孩(男人)
{
return woman->CreatAMan();
}
Woman * GetAGirl( Woman * woman ) //让女人生女孩(女人)
{
return woman->CreatAWoman();
}
friend NvWa;
friend Woman;
};
Man * NvWa::CreatAMan( void )
{
return new Man;
}
Woman * NvWa::CreatAWoman( void )
{
return new Woman;
}
Man * Woman::CreatAMan( void )
{
return new Man;
}
Woman * Woman::CreatAWoman( void )
{
return new Woman;
}
void main()
{
NvWa nvwa; //女娲
Man * man = nvwa.CreatAMan(); //创造了一个男人
Woman * woman = nvwa.CreatAWoman(); //创造了一个女人
Man * boy = man->GetABoy( woman ); //男人让女人生了一个男孩
Woman * girl = man->GetAGirl( woman ); //又让女人生了一个女孩
delete man;
delete woman;
delete boy;
delete girl;
}
其实细心的朋友不难发现,创建人可以抽象成一个行为,男人和女人都是人,但是这样做不知道生下来的是男孩或是女孩,因为继承的特性不能由其基类来得知派生类的信息,这个问题在C++中也有解决的办法,我会在以后的文章中介绍。
2001/9/18
丁宁
本文地址:http://com.8s8s.com/it/it29937.htm