c++构造与析构

构造函数


  1. 使用类定义变量时调用的特殊成员函数,声明类变量时自动调用.

  2. 定义格式:无返回类型,且函数名与类名相同,参数用于初始化类成员,根据不同需求可以定义重载和默认形式参数.

  3. 定义在类外时需要在函数名前放上类空间(eg:classname::classname(...)

  4. 当定义类时,如果没有手动定义构造函数,系统会自动隐式生成缺省的默认构造函数 [ 0 ] ^{[0]} [0],当然,也可显式声明缺省的构造函数,方法有两种

    //直接声明:	
    class classname{
    classname( ){ };
    };
    //使用default关键字[1]:
    class classname{
    classname() = default;
    };
    
  5. 构造函数用于成员赋初值,一般在函数右括号后加上:然后以 类成员1(形参1) 的形式直接赋初值也可以用 = 在函数体内赋值(在函数体内只是单纯的赋值,并非初始化),不同于一般函数,构造函数具体分为两个阶段。

    • 初始化阶段:在形参传入后,函数体执行前,以参数列表的形式为某些特定类成员赋初值(如:父类构造函数,对象成员,常成员,引用成员 [ 2 ] ^{[2]} [2]等必须在初始化阶段进行)
    • 函数体阶段:如一般函数一样在函数体中执行相应的操作,值得注意的是初始化阶段进行初始化的成员变量在进入函数体时就已经完成初始化而可以直接使用了。
  6. 定义了构造函数的类在实例化时可以以 类变量1(实参1,…) 的方式赋初值,当参数只有一个时也可以用 类变量1=实参 的形式赋初值。

    // MyClass1 需要三个整性构造参数
    // MyClass2 需要一个整性构造参数
    MyClass1 myclass1 = MyClass1(1,2,3);
    MyClass2 myclass2 = 1;
    
  7. 不声明变量,直接调用构造函数将声明匿名的类对象.

    std::cout << MyClass(1,2,3) << std::endl;
    
  8. 就地初始化:c++11标准开始支持在类中声明数据成员的同时进行初始化(c++11以前只支持静态成员这样做)

    //e.g.
    class classname{
        int a = 5;
    };
    //注意:
    //就地初始化无法使用()
    class classname{
        int a(5);  // ×
    };
    //就地初始化无法自动推断数组大小
    class classname{
        int arr[]={1,2,3,4};   // ×
    };
    
  9. 初始化顺序:类成员初始化方式现在有三种:就地初始化、构造函数初始化列表、构造函数体中使用=。(实际上最后一种属于赋值而非初始化,理解为在构造函数体中对未初始化的数据成员通过形参赋值),其调用顺序为:就地初始化 --> 构造函数初始化列表 --> 构造函数体中使用= [ 3 ] ^{[3]} [3](实例化后的成员的值取决于直到构造函数执行完后最后一次值的改变)

注[0]:默认构造函数指调用时不需要参数传递的构造函数,即既可以指没有形参声明的构造函数,也可以指存在形参声明但形参均有形参默认值的声明的构造函数

注[1]:使用default关键字可以显式告诉编译器生成一个默认的构造函数,与之相对的,以同样的方式使用delete关键字则可以显式告诉编译器不要声明默认的构造函数(同理,针对默认的析构函数,拷贝构造函数以及=运算符重载也可以使用上述关键字显式 生成 或者 禁止 )

注[2]:对于引用成员、常成员等这类需要在声明时就给出初始值的数据类型类型,在类中定义同时可以不用给出,构造函数的初始化阶段会被等效于在声明的同时赋初值(若通过构造函数对引用初始化,构造函数的形参只能传入需要创建别名的变量的引用,而不是变量本身,因为后者是值传递,此时对引用成员的初始化实际是在为某一临时形参空间创建别名,传入变量指针是可行的,但在初始化时应注意在形参前加上*,即为指针所指向的内容所在空间创建别名,否则将为指针所在的临时形参空间创建别名)

注[3]:实际上,当同一个成员的初始化过程中同时存在就地初始化和初始化列表时,编译器会忽略就地初始化的声明而直接采用初始化列表对其初始化

拷贝构造函数


  1. 用已有类变量给新声明的类变量直接赋初值的类成员函数
  2. 定义格式:基本同构造函数格式相同,但形参类型固定为类变量的常引用(对象引用),是构造函数的重载
  3. 当未定义拷贝构造函数时定义类,系统会自动生成一个缺省的拷贝构造函数,大多数情况下可以正常工作,但当涉及到动态空间分配(new或malloc)时会出问题,原因是自定义拷贝构造函数采用位拷贝的形式绝对拷贝,故当使用原变量初始化新变量时不会为新变量再次动态分配空间,而是将原来变量分配的空间地址拷贝到新变量,故两个类变量实际使用了同一个动态分配的空间,同时在类变量释放前调用析构函数释放分配空间时就会对同一空间释放两次,出现错误,故涉及指针成员一般选择自定义拷贝构造函数(也即是深浅拷贝的问题)
  4. 用原有变量对新的变量赋初值可采用圆括号直接赋初值,也可用赋值运算符赋初值,二者都是在执行对拷贝构造函数的调用

代理构造


  1. 与Java中的委托类似,说白了就是在类中一个构造函数如果做的事和另一个构造函数相同,或差别不大时,可以在该构造函数的初始化列表中调用另一个构造函数来完成构造

  2. 格式: 注意只能在初始化列表处进行调用,且必须避免形成环形构造委托链(当多个构造函数按顺序依次委托调用时称之为委托构造链,且该链必须得有终止的构造函数,不能出现后面的构造函数返回去调用之前的委托构造函数的情况,否则将会出现死循环,程序将崩溃)

    //e.g.正确调用:
    class example{
        int a;
    public:
        example(int a_){
            //...
        }
        example(example& boj) : example(boj.a){};
    };
    //e.g.错误调用:
    class error{
        int a;
    public:
        error(int a_) : error(){
            //...
        }
        error() : error(5){
            //...
        }
    

析构函数


  1. 生存期满系统释放类变量空间前自动调用的成员函数,多用于类中申请空间的delete
  2. 定义格式:无返回类型,无形式参数,函数名为类名前加上~符号,不可定义重载
  3. 定义在类外时需要在函数名前放上类空间(eg: classname ::~classname()
  4. 当没有定义析构函数定义类时,系统会自动生成一个缺省内容的空析构函数,也可显式声明缺省的析构函数,方法同上述构造函数
  5. 析构函数不用调用,在类变量空间释放前会被自行调用
  6. 析构函数必须为公有成员(public)
  7. 当析构函数同时声明为纯虚函数时,需要给出函数体实现,将析构函数声明为纯虚函数的情况将在纯虚函数的介绍中给出说明。
    class MyClass{
    public:
    	virtual ~MyClass() = 0 {
    		//do something
    	}
    }
    
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值