• 第17.18.19课.对象的构造


    1.思考:对象中成员变量的初始值是多少?

    上创建对象时,成员变量初始为随机值
    上创建对象时,成员变量初始为随机值
    静态存储区上创建对象时,成员变量初始为0值

    #include <stdio.h>
    
    class Test
    {
    private:
        int i;
        int j;
    public:
        int getI() 
        {
            return i;
        }
        
        int getJ()
        {
            return j;
        }
    };
    
    Test gt;               //静态存储区
    
    int main()
    {
        printf("gt.i = %d
    ", gt.getI());
        printf("gt.j = %d
    ", gt.getJ());
        
        Test t1;            //栈区
        
        printf("t1.i = %d
    ", t1.getI());
        printf("t1.j = %d
    ", t1.getJ());
        
        Test* pt = new Test;        //堆区
        
        printf("pt->i = %d
    ", pt->getI());        //这里恰好是0,只能说运气
        printf("pt->j = %d
    ", pt->getJ());        //同上
        
        delete pt;
        
        return 0;
    
    }
    

    2.构造函数

    c++中可以定义与类名相同的特殊成员函数。这种特殊的成员函数叫做构造函数。
    a.构造函数没有任何返回类型的声明。
    b.构造函数在对象定义时自动被调用。(定义会调用构造函数,赋值不会调用构造函数)

    class Test
    {
    private:
        int i;
        int j;
    public:
        int getI() 
        {
            return i;
        }
        
        int getJ()
        {
            return j;
        }
        
        Test()        //构造函数
        {
            i = 1;
            j = 2;
        }
    };
    

    3.带有参数的构造函数

    a.一个类中可以存在多个重载的构造函数。
    b.构造函数的重载遵循c++重载的规则。

    class Test
    {
    public:
        Test()
        {
            printf("Test()
    ");
        }
        
        Test(int v)
        {
            printf("Test(int v), v = %d
    ", v);
        }
    };
    
    int main()
    {
        Test t;         //调用 Test()
        Test t1(1);     //调用 Test(int v)
        Test t2 = 2;    //调用 Test(int v)
    
        t = t2;        //最后结果只显示了上面的三个。没有显示这个,是因为构造函数只有在定义的时候(初始化)才调用,其他时候不被调用。
    }
    

    注意:

    对象定义和对象声明不同:
    对象定义:申请对象的空间并调用构造函数(时间存在的)
    对象声明:告诉编译器存在这样一个对象(虚无的)

    4.手工调用构造函数

    一般情况下构造函数在对象定义时被自动调用;特殊情况下,需要手工调用构造函数。

    #include <stdio.h>
    
    class Test
    {
    private:
        int m_value;
        
    public:
        Test()
        {
            printf("Test()
    ");
            
            m_value = 0;
        }
        
        Test(int v)
        {
            printf("Test(int v), v = %d
    ", v);
        
            m_value = v;
        }
        
        int getValue()
        {
            return m_value;
        }
    };
    
    int main()
    {
        Test ta[3] = {Test(), Test(1), Test(2)};        //手工调用构造函数
           
        for(int i=0; i<3; i++)
        {
            printf("ta[%d].getValue() = %d
    ", i , ta[i].getValue());
        }
        
        Test t = Test(100);        //手工调用构造函数
        
        printf("t.getValue() = %d
    ", t.getValue());
        
        return 0;
    }
    

    5.特殊的构造函数

    一个类需要:构造函数和拷贝构造函数

    a.无参构造函数

    没有参数的构造函数
    当类中没定义构造函数时,编译器默认提供一个无参的构造函数,并且其函数体为空

    b.拷贝构造函数

    参数为const class_name&的构造函数
    当类中没有定义拷贝构造函数时,编译器提供一个拷贝构造函数,课进行简单的变量的值的赋值(默认即定义了拷贝构造函数后,编译器不会继续给类提供无参构造函数)

    浅拷贝:拷贝后对象的物理状态相同。(单纯的,暴力的复制)(编译器默认提供的就是浅拷贝)
    深拷贝:拷贝后对象的逻辑状态相同。(复制后能达到自己想要实现的逻辑,涉及到内存就需要使用深拷贝)

    eg:
    浅拷贝:

    #include <stdio.h>
    
    class Test
    {
    private:
        int i;
        int j;
        int *p;
        
    public:
        int getI()
        {
            return i;
        }
        
        int getJ()
        {
            return j;
        }
        
        int *getP()
        {
            return p;
        }
        
        Test(int v)
        {
            i = 1;
            j = 2;
            p = new int;
            
            *p = v;
        }
    };
    
    int main()
    {
        Test t1(3);
        Test t2 = t1;
        
        printf("t1.i = %d, t1.j = %d, t1.p = %d
    ", t1.getI(), t1.getJ(), t1.getP());
        printf("t2.i = %d, t2.j = %d, t2.p = %d
    ", t2.getI(), t2.getJ(), t2.getP());
        
        return 0;
    
    }
    


    当在类中加入内存释放时

    void free()
    {
        delete p;
    }
    

    出现错误。我们把一个地址中的内存释放了两次。

    深拷贝:

    #include <stdio.h>
    
    class Test
    {
    private:
        int i;
        int j;
        int *p;
        
    public:
        int getI()
        {
            return i;
        }
        
        int getJ()
        {
            return j;
        }
        
        int *getP()
        {
            return p;
        }
        
        Test(int v)
        {
            i = 1;
            j = 2;
            p = new int;
            
            *p = v;
        }
        
        Test(const Test& t)
        {
            i = t.i;
            j = t.j;
            
            p = new int;
            
            *p = *t.p;
        }
        
        void free()
        {
            delete p;
        }
    };
    
    int main()
    {
        Test t1(3);
        Test t2 = t1;
        
        printf("t1.i = %d, t1.j = %d, *t1.p = %d
    ", t1.getI(), t1.getJ(), *t1.getP());
        printf("t2.i = %d, t2.j = %d, *t2.p = %d
    ", t2.getI(), t2.getJ(), *t2.getP());
        
        t1.free();
        t2.free();
        
        return 0;
    
    }
    

    6.什么时候需要进行深拷贝?

    对象中有成员代指了系统中的资源时
    a.成员指向了动态内存空间;
    b.成员打开了外存中的文件;
    c.成员使用了系统中的网络端口;

    一般原则:
    只要自定义拷贝构造函数,必然是深拷贝。不然就使用默认的。

  • 相关阅读:
    Liferay7 BPM门户开发之34: liferay7对外服务类生成(RestService Get Url)
    Liferay7 BPM门户开发之33: Portlet之间通信的3种方式(session、IPC Render Parameter、IPC Event、Cookies)
    Liferay7 BPM门户开发之32: 实现自定义认证登陆(定制Authentication Hook)
    Liferay7 BPM门户开发之30: 通用帮助类Validator、ArrayUtil、StringUtil等使用
    Liferay7 BPM门户开发之29: 核心kernel.util包下面的通用帮助类ParamUtil、GetterUtil使用
    Liferay7 BPM门户开发之28: Portlet文件上传,及实体类同步更新上传
    Liferay7 BPM门户开发之26: 集成Activiti到Liferay7
    Liferay7 BPM门户开发之27: MVC Portlet插件工程开发
    Liferay7 BPM门户开发之25: Liferay7应用程序配置(APPLICATION CONFIGURATION)
    Liferay7 BPM门户开发之24: Liferay7应用程序安全
  • 原文地址:https://www.cnblogs.com/huangdengtao/p/11812992.html
Copyright © 2020-2023  润新知