zoukankan      html  css  js  c++  java
  • 构造函数

    1 构造函数

        1)名字和类名相同。

        2)不能定义返回类型,参数个数可以有任意个。

        3)如果未定义构造函数,系统会自动产生一个默认构造函数。但只要程序中有构造函数的定义,系统就不会再自动产生默认构函。

    2 转换构造函数

        只有一个参数的构造函数称为转换构造函数。转换构造函数可以将其他类型转换成类类型。类的构造函数只有一个参数是比较危险的,因为编译器可能隐式的把参数类型转换成类类型。为了避免隐式转换,可以在构造函数前加上explicit,这样编译器就不会隐式调用了。

        假如有一个Test类,他有一个转换构造函数:

        Test(int num) :num_(num)
        {
            cout << "Test(int num)" <<num<< endl;
        }
        ~Test()
        {
            cout << "~Test()" << num_<<endl;
        }

        执行代码:

    int main()
    {
        Test t(10);
        //t = 20;
        return 0;
    }

        执行结果:

       这个程序中,使用了转换构造函数“构造”的功能,并未用到其“转换”功能。将代码更改如下:

    int main()
    {
        Test t(10);
        t = 20;
        return 0;
    }

        执行结果:

        这里首先调用构造函数对t完成初始化。接着将20赋给t显然类型不符,本应该出错,可是程序中定义了转换构造函数,编译器会隐式的把20作为参数构造出来一个临时的Test对象(如果构造函数前加了explicit关键字,就不能隐式的调用,从而这里出错)。将其赋值给t后,该临时对象需要被析构,因此调用了一次析构函数。最后栈上的t被析构。需要注意的是将临时对象赋给t时,还调用了赋值运算符函数;如果在定义的时候就用一个同类型的对象对其进行初始化,则调用后面将要介绍的拷贝构造函数。

    3 构造函数初始化列表

        可以使用初始化列表来对类成员初始化,通过 : 调出初始化列表。放在初始化列表才是真正的初始化,属于初始化段。在函数体中进行初始化实际上是赋值,属于普通计算段。因此,由于const成员和引用要求定义时必须初始化,所以二者的初始化必须放在初始化列表。

        初始化列表还有一个作用:

    class Object
    {
    public:
        Object(int num):num_(num)
        {
            cout<<"Object..."<<num<<endl;
        }
        ~Object()
        {
            cout<<"~Object..."<<endl;
        }
    private:
        int num_;
    };
    
    class Container
    {
    public:
        Container()
        {
            cout<<"Container..."<<endl;
        }
        ~Container()
        {
            cout<<"~Container..."<<endl;
        }
    private:
        Object obj_;
    };

    上述代码在实例化Container类对象时会报错。这是因为,Container类对象的成员是Object类对象。因此,在初始化时,需先调用Object的默认构造函数,但Object中没有默认构造函数,而且由于Object中定义了一个带参数的构造函数,所以编译器也不会自动生成一个默认构造函数。要解决这个问题,可以在初始化obj_的时候,调用Object中定义的那个构造函数,而这个调用就在Container的初始化列表中实现:

    Container():obj_(0)
    {
    cout<<"Container..."<<endl;
    }
    
    Container(num):obj_(num)
    {
    cout<<"Container..."<<endl;
    }

    4 拷贝构造函数

        拷贝构造函数的参数必须是对该类类型的引用,它在两种情况用到:1)当用一个类对象去初始化另一个同类类对象时。2)函数的参数是类对象或者返回值是类对象时。

        (1)如果函数的参数是类对象,在函数结束时,还会调用析构函数将其析构掉;(2)如果参数是类对象的引用,则不会调用拷贝构造函数,也不会调用析构函数。(3)当函数的返回值是类对象时,首先会调用拷贝构造函数。但是,如果用它去初始化一个刚刚定义的类对象,则不会再次调用拷贝构造函数,这是因为相当于给返回的临时对象更了名,例如:Test t=TestFunc();如果没人接收,只是TestFunc(),那么它会立即调用析构函数。(感觉从返回类对象,对该对象接收或不接收,到最终的销毁,都是经历了一次拷贝构造和一次析构)。

        当用一个对象去给另一个对象赋值时,会调用赋值运算符函数;当用一个对象去初始化另一个对象时,会调用拷贝构造函数。一定要区分赋值和初始化,才能知道需要调用哪个函数。类最好定义自己的拷贝构造函数与赋值运算符函数,否则可能会导致浅拷贝(当类包含动态成员时)。浅拷贝会引起内存被析构两次等严重问题。

  • 相关阅读:
    POST请求
    怎样在ios开发中设置tableview的cell颜色
    error LNK2005 已经在***.obj中定义
    IOS开发之UIView的基本使用
    [置顶] 浅谈Android的资源编译过程
    IOS开发之UIView总结
    鉴别不使用的索引
    浅谈Jquery的使用下篇
    Go如何使用实现继承的组合
    做一个小淘气轮廓--文章和论文专辑
  • 原文地址:https://www.cnblogs.com/lulu10922/p/5818654.html
Copyright © 2011-2022 走看看