快捷搜索:   服务器  安全  linux 安全  MYSQL  dedecms

C++面向对象编程入门:构造函数与析构函数(2)

    #include <iostream>
    #include <string>
    using namespace std;
    class Teacher
    {
        public:
        Teacher(char *temp)
        {
            director = new char[10];
            strcpy(director,temp);
        }
        ~Teacher()
        {
            cout<<"释放堆区director内存空间1次";
            delete[] director;
            cin.get();
        }
        char *show();
        protected:
        char *director;
    };
    char *Teacher::show()
    {
        return director;
    }
    class Student
    {
        public:
        Student()
        {
            number = 1;
            score = 100;
        }
        void show();

        protected:
        int number;
        int score;
        Teacher teacher("王大力");//错误,一个类的成员如果是另外一个类的对象的话,不能在类中使用带参数的构造函数进行初始化

    };

    void Student::show()
    {
        cout<<teacher.show()<<endl<<number<<endl<<score<<endl;
    }
    void main()
    {
        Student a;
        a.show();
        Student b[5];
        for(int i=0; i<sizeof(b)/sizeof(Student); i++)
        {
            b[i].show();
        }
        cin.get();
    }

      可是很遗憾,程序不能够被编译成功,为什么呢?

      因为:类是一个抽象的概念,并不是一个实体,并不能包含属性值(这里来说也就是构造函数的参数了),只有对象才占有一定的内存空间,含有明确的属性值!

      这一个问题是类成员初始化比较尴尬的一个问题,是不是就没有办法解决了呢?呵呵。。。。。。

      c++为了解决此问题,有一个很独特的方法,下一小节我们将介绍。

      对于上面的那个"尴尬"问题,我们可以在构造函数头的后面加上:号并指定调用哪那个类成员的构造函数来解决!

      教程写到这里的时候对比了很多书籍,发现几乎所有的书都把这一章节叫做构造类成员,笔者在此觉得有所不妥,因为从读音上容易混淆概念,所以把这一小节的名称改为构造类的成员比较合适!


      代码如下:

    #include <iostream>
    using namespace std;
    class Teacher
    {
        public:
        Teacher(char *temp)
        {
            director = new char[10];
            strcpy(director,temp);
        }
        ~Teacher()
        {
            cout<<"释放堆区director内存空间1次";
            delete[] director;
            cin.get();
        }
        char *show();
        protected:
        char *director;
    };
    char *Teacher::show()
    {
        return director;
    }
    class Student
    {
        public:
        Student(char *temp):teacher(temp)
        {
            number = 1;
            score = 100;
        }
        void show();

        protected:
        int number;
        int score;
        Teacher teacher;

    };

    void Student::show()
    {
        cout<<teacher.show()<<endl<<number<<endl<<score<<endl;
    }
    void main()
    {
        Student a("王大力");
        a.show();
        //Student b[5]("王大力");  //这里这么用是不对的,数组不能够使用带参数的构造函数,以后我们将详细介绍vector类型
        // for(int i=0; i<sizeof(b)/sizeof(Student); i++)
        //{
        //    b[i].show();
        //}
        cin.get();
    }

      大家可以发现最明显的改变在这里

    Student(char *temp):teacher(temp)

      冒号后的teacher就是告诉调用Student类的构造函数的时候把参数传递给成员teacher的Teacher类的构造函数,这样一来我们就成功的在类体外对teacher成员进行了初始化,既方便也高效,这种冒号后指定调用某成员构造函数的方式,可以同时制定多个成员,这一特性使用逗号方式,例如:

    Student(char *temp):teacher(temp),abc(temp),def(temp)

      由冒号后可指定调用哪那个类成员的构造函数的特性,使得我们可以给类的常量和引用成员进行初始化成为可能。

     我们修改上面的程序,得到如下代码:

    #include <iostream>
    #include <string>
    using namespace std;
    class Teacher
    {
        public:
        Teacher(char *temp)
        {
            director = new char[10];
            strcpy(director,temp);
        }
        ~Teacher()
        {
            cout<<"释放堆区director内存空间1次";
            delete[] director;
            cin.get();
        }
        char *show();
        protected:
        char *director;
    };
    char *Teacher::show()
    {
        return director;
    }
    class Student
    {
        public:
        Student(char *temp,int &pk):teacher(temp),pk(pk),ps(10)
        {
            number = 1;
            score = 100;
        }
        void show();

        protected:
        int number;
        int score;
        Teacher teacher;
        int &pk;
        const int ps;

    };

    void Student::show()
    {
        cout<<teacher.show()<<endl<<number<<endl<<score<<endl<<pk<<endl<<ps<<endl;
    }
    void main()
    {
        char *t_name="王大力";
        int b=99;
        Student a(t_name,b);
        a.show();
        cin.get();
    }

      改变之处最重要的在这里Student(char *temp,int &pk):teacher(temp),pk(pk),ps(10)

      调用的时候我们使用

    Student a(t_name,b);

      我们将b的地址传递给了int &pk这个引用,使得Student类的引用成员pk和常量成员ps进行了成功的初始化。


      但是细心的人会发现,我们在这里使用的初始化方式并不是在构造函数内进行的,而是在外部进行初始化的,的确,在冒号后和在构造函数括号内的效果是一样的,但和teacher(temp)所不同的是,pk(pk)的括号不是调用函数的意思,而是赋值的意思,我想有些读者可能不清楚新标准的c++对变量的初始化是允许使用括号方式的,int a=10和int a(10)的等价的,但冒号后是不允许使用=方式只允许()括号方式,所以这里只能使用pk(pk)而不能是pk=pk了。

      这一小节的内容是说对象构造的顺序的,对象构造的顺序直接关系程序的运行结果,有时候我们写的程序不错,但运行出来的结果却超乎我们的想象,了解c++对对象的构造顺序有助于解决这些问题。

      c++规定,所有的全局对象和全局变量一样都在主函数main()之前被构造,函数体内的静态对象则只构造一次,也就是说只在首次进入这个函数的时候进行构造!

      代码如下:

    #include <iostream>
    #include <string>
    using namespace std;

    class Test
    {
    public:
        Test(int a)
        {
            kk=a;
            cout<<"构造参数a:"<<a<<endl;
        }
    public:
        int kk;
    };

    void fun_t(int n)
    {
        static Test a(n);
        //static Test a=n;//这么写也是对的
        cout<<"函数传入参数n:"<<n<<endl;
        cout<<"对象a的属性kk的值:"<<a.kk<<endl;
    }
    Test m(100);
    void main()
    {
        fun_t(20);
        fun_t(30);
        cin.get();
    }

      下面我们来看一下,类成员的构造顺序的问题。

      先看下面的代码:

    #include <iostream>
    using namespace std;

    class Test
    {
    public:
        Test(int j):pb(j),pa(pb+5)
        {

        }
    public:
        int pa;
        int pb;
    };
    void main()
    {
        Test a(10);
        cout<<a.pa<<endl;
        cout<<a.pb<<endl;
        cin.get();
    }

      上面的程序在代码上是没有任何问题的,但运行结果可能并不如人意。

      pa并没有得到我们所希望的15而是一个随机的任意地址的值。

      这又是为什么呢?

      类成员的构造是按照在类中定义的顺序进行的,而不是按照构造函数说明后的冒号顺序进行构造的,这一点需要记住!

顶(0)
踩(0)

您可能还会对下面的文章感兴趣:

最新评论