zoukankan      html  css  js  c++  java
  • 6. 对象特性之构造函数和析构函数


    对象的初始化和处理也是两个非常重要的安全问题,C++利用构造函数和析构函数解决上述问题。这两个函数会被编译器自动调用,如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现。

    • 构造函数:主要作用域创建对象时为对象成员属性赋值,构造函数由编译器自动调用,无须手动调用
    • 析构函数:主要作用于销毁前系统自动调用,执行一些清理工作

    一、构造函数和析构函数

    1. 构造函数语法:类名(){}

    • 构造函数,没有返回值也不写void
    • 函数名称于类名相同
    • 构造函数可以由参数,因此可以发生重载
    • 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

    2. 析构函数语法:~类名(){}

    • 析构函数,没有返回值也不写void
    • 函数名称于类名相同,在名称前加上符号~
    • 析构函数不可以由参数,因此不可以发生重载
    • 程序在对象**销毁前*会自动调用析构,无须手动调用,而且只会调用一次
    #include <iostream>
    using namespace std;
    
    //对象的初始化和处理
    class Person 
    {
    public:
        //1.构造函数,初始化操作
        Person()
        {
            cout <<"Person 构造函数的调用" <<endl;  //如果我们不写,此函数就为空
        } 
        
        //2.析构函数,进行清理操作
        ~Person()
        {
            cout <<"Person的析构函数调用" <<endl;//如果我们不写,此函数就为空
        }
    };
    
    //小结:构造和析构都是==必须有的实现==,如果我们自己不提供,编译器会提供一个空实现的构造和析构
    void test01()
    {
        Person p; //局部变量,在栈上的数据,test01执行完毕后,释放这个对象
    }
    int main()
    {
        test01(); //析构、构造都调用
        Person p;// 在全部执行完后,调用析构函数
        system("pause");
        return 0;
    }
    

    二、构造函数的分类及调用

    两种分类方式:

    • 按参数分为:有参构造和无参构造
    • 按类型分为:普通构造和拷贝构造

    三种调用方式:

    • 括号法
    • 显示法
    • 隐式转换法
    #include <iostream>
    using namespace std;
    //1. 构造函数的分类及调用
    class Person 
    {
    public:
        //构造函数,类型有参与无参(默认构造)
        Person()
        {
            cout <<"Person 无参(默认)构造函数的调用" <<endl;  
        }
        Person(int a)
        {
            age = a;
            cout <<"Person 有参构造函数的调用" <<endl;  
        }  
        //拷贝构造函数
        Person(const Person &p)
        {
            age = p.age; //将传入人身上的所有属性,拷贝到我自己身上
            cout <<"Person 拷贝构造函数的调用" <<endl;  
        }
        ~Person()
        {
            cout <<"Person的析构函数调用" <<endl;//如果我们不写,此函数就为空
        }
        int age;
    };
    //调用
    void test01()
    {
        //1.括号发
        Person p; //默认构造函数调用
        Person p2(10); //有参构造函数
        Person p3(p2); //拷贝构造函数
        cout << "p2的年龄" << p2.age << endl;
        cout << "p3的年龄" << p3.age << endl; //拷贝构造函数复制全部属性
    
        //2.显示法
        Person p1;
        Person p2 = Person(10); //有参构造
        Person p3 = Person(p2); //拷贝构造
        
        //3.隐式转换法
        Person p4=10; //相当于写了 Person p4 = Person(10);有参构造
        Person p5=p4;//拷贝构造
    }
    int main()
    {
        test01();
        system("pause");
        return 0;
    }
    

    注意事项:

    • 调用默认构造函数时,不要加(),因为Person p();,编译器会认为这是一个函数的声明
    • 显示法中,单独拿出右侧对象(eg:Person(10))是匿名对象。特点:当前执行结束后,系统会立刻回收匿名对象
    • 不要利用拷贝构造函数 初始化匿名对象,例如编译器会认为Person (p3)等价于Person p3;

    1. 拷贝构造函数

    C++中拷贝构造函数的调用时机通常由三种情况

    • 使用一个已经创建完毕的调用对象来初始化一个新对象
    • 值传递的方式给函数参数传值
    • 值方式返回局部对象
    • 由于编辑器和版本的不同,地址返回可能会有所不同
    #include <iostream>
    using namespace std;
    class Person 
    {
    public:
        Person()
        {
            cout << "person 默认构造函数调用"<<endl;
        }
        Person(int age)
        {
            cout << "person 有参构造函数调用"<<endl;
            m_age=age;
        }
        Person(const Person &p)
        {
            cout << "person 拷贝构造函数调用"<<endl;
            m_age = p.m_age;
        }
        ~Person()
        {
            cout << "person 析构函数调用"<<endl;
        }
        int m_age;
    };
    //1. 使用一个已经创建完毕的调用对象来初始化一个新对象
    void test01()
    {
        Person p1(20);
        Person p2(p1);
        cout << "p2的年龄" << p2.m_age << endl;
    }
    //2. 值传递的方式给函数参数传值
    void dowork(Person p)
    {
    }
    void test02()
    {
        Person p;
        dowork(p); //值传递方式,形参不影响实参
    }
    //3. 以值方式返回局部对象
    Person dowork2()  //返回值为p1的拷贝对象
    {
        Person p1;
        cout << (int*)&p1 << endl;
        return p1;
    }
    void test03()
    {
        Person p = dowork2();
        cout << (int*)&p << endl;
    }
    int main()
    {
        test01();
        test02();
        test03();
        system("pause");
        return 0;
    }
    

    2. 构造函数的调用规则

    默认情况下,C++编译器至少给一个类添加3个函数

    • 默认构造函数(无参,函数体为空
    • 默认析构函数(无参,函数体为空
    • 默认拷贝构造函数,对属性进行值拷贝


      构造函数调用规则如下:
    • 如果用户定义有参构造函数,C++不再提供默认无参构造,但会提供拷贝构造;
    • 如果用户定义拷贝构造函数,对属性进行值拷贝,C++不会提供其他构造函数;


    3. 深拷贝与浅拷贝

    浅拷贝:简单的赋值拷贝操作 ————> 问题:由于栈的先进后出,堆区的重复释放
    深拷贝:在堆区重新申请空间,进行拷贝操作 ————> 解决浅拷贝带来的问题
    小结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

    #include <iostream>
    using namespace std;
    class Person 
    {
    public:
        Person()
        {
            cout << "person 默认构造函数调用"<<endl;
        }
        Person(int age,int height)
        {
            m_age=age;
            m_height=new int (height)
            cout << "person 有参构造函数调用"<<endl;
        }
        Person(const Person &p)
        {
            cout << "person 拷贝构造函数调用"<<endl;
            m_age = p.m_age;
            m_height = p.m_height; //编译器默认实现就是这行代码
            //深拷贝操作
            m_height=new int (*p.m_height); //重新开辟一块堆区内存
        }
        ~Person()
        {
            //析构代码,将堆区开辟数据做释放操作
            if (m_height != NULL)
            {
                delete m_height;
                m_height = NULL; //防止野指针
            }
            cout << "person 析构函数调用"<<endl;
        }
        int m_age; //年龄
        int *m_height; //身高,堆区数据
    };
    void test01()
    {
        Person p1(18);
        cout << "p1的年龄:" << p1.m_age <<"身高为:"<<*p1.m_height<< endl;
        Person p2(p1);
        cout << "p2的年龄:" << p2.m_age <<"身高为:"<<*p2.m_height<< endl;
    }
    int main()
    {
        test01();
        system("pause");
        return 0;
    }
    
    哪有什么胜利可言,坚持意味着一切
  • 相关阅读:
    vs2008打开aspx文件时设计界面死机情况的解决
    数据库设计知识点
    JS从样式表取值的函数currentStyle(IE),defaultView(FF)
    Iframe选区
    实用正则表达式(实用篇)
    46.class属性 Walker
    410.锚链接和空链接 Walker
    45.ID属性 Walker
    49.文件下载 Walker
    47.title和style属性 Walker
  • 原文地址:https://www.cnblogs.com/BY1314/p/12768821.html
Copyright © 2011-2022 走看看