女娲造人----构造析构与对象的生死

来源:互联网 发布:淘宝手淘首页是什么 编辑:程序博客网 时间:2024/04/29 01:02

女娲造人

                                                ----构造析构与对象的生死

作者:HolyFire

 

在《由始至终----构造与析构》最后我提到了C++中的构造函数和析构函数与对象的生和死有着很大的关系,好好运用还能控制对象的生死,使得编程的思路更接近现实世界的规律。

 

我们先回忆一下,在对象创建的时候,合适的构造函数将被自动调用,而对象销毁的时候,析构函数将被自动调用。在构造函数与析构函数可以被随意调用的情况下,也就是被声明为public的时候,这没有任何问题,但是一旦我们将他们的可视性改为privateprotected呢,那么情况就有所改观了。

 

声明为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

丁宁