C++对象的构造和析构函数(初始化、清理、深浅拷贝、初始化列表)

大纲

1.构造函数(作用是:初始化,初始化,初始化,初始化,只有初始化的时候调用
 1)无参数构造函数( 调用方法: Test t1, t2;)
 2)有参数构造函数(调用方法:调用的三种方法,这三种方法都是一样的)
  Test5 t1(10,20); //c++编译器默认调用有参构造函数 括号法
   Test5 t3 = (20, 10); //c++编译器默认调用有参构造函数 等号法(C++对等号符进行了功能加强)这里用得是逗号表达式。
   Test5 t4 = Test5(30); //程序员手工调用构造函数 产生了一个对象 直接调用构造函数法

 3)copy构造函数 (用一对象初始化另一对象时,C++编译器会自动调用这个对象的Copy构造函数。)
 4)copy构造函数的四种调用场景:
 第一种:Test4 t2 = t1; //用t1来初始化 t2
 第二种:Test4 t2(t1); //用t1对象 初始化 t2对象
 第三种:函数调用时:当形参是一个类的时候,函数调用时,用实参去初始化形参,会调用copy构造函数。
 第四种:C++中函数返回值是一个对象时。对应有下面几种情况:
(1). 函数的返回值是一个元素 (复杂类型的), 编译器自己创建一个新的匿名对象,然后返回这个新的匿名对象,所以会调用匿名对象类的copy构造函数。如果只是调用,没有变量去接匿名对象的返回值,调用完就会马上被析构掉。
 (2)用这个返回的新匿名对象去初始化一个对象(Location m = g(); ),会直接将返回的匿名对象转换成m, 因为返回值直接变成了m,所以不会调用构造函数和析构函数。这是编译器的规定,无需解释。
 (3)用这个返回的新匿名对象赋值给一个对象(m2 = g();),注意不是初始化。生命周期结束后会调用析构函数。

注意:在VS编译器里是用创建新的匿名对象的方式返回。而在Linux平台上,是不会产生了匿名对象,直接返回,提高了执行效率。从而原本仅在fun函数内有效(局部变量生存周期)的t对象,由于被返回,在test1函数中仍然有效。

2.析构函数(清理内存)
 生命周期结束(函数结束)的时候就会调用析构函数。

 析构函数执行顺序:(在程序顺序角度:先从后面执行的程序开始往上析构–>出栈)
在内存模型中有一块区域叫做栈区,它是由系统维护的(程序员无法操作),用来存储函数的参数、局部变量等,类似于数据结构中的栈,也是先进后出。
 当遇到函数调用时,首先将下一条指令的地址压入栈区,然后将函数参数压入栈区,随着函数的执行,再将局部变量(或对象)按顺序压入栈区。
 栈区是先进后出的结构,当函数执行结束后,先把最后压入的变量(或对象)弹出,以此类推,最后把第一个压入的变量弹出。接下来,再按照先进后出的规则弹出函数参数,弹出下一条指令地址。有了下一条指令的地址,函数调用结束后才能够继续执行后面的代码。
 所谓弹出变量,就是销毁变量,清空变量所占用的资源。如果这个变量是一个对象,那么就会执行析构函数。

3.构造函数调用规则研究
1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数
2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数
3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数
4 )默认拷贝构造函数:当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制。
总结:构造函数设计规则,只要你写了构造函数,那么你必须调用。多个时候至少用一个。

4.构造析构阶段性总结
1)构造函数是C++中用于初始化对象状态的特殊函数
2)构造函数在对象创建时自动被调用
3)构造函数和普通成员函数都遵循重载规则
4)拷贝构造函数是对象正确初始化的重要保证
5)必要的时候,必须手工编写拷贝构造函数

5.浅拷贝和深拷贝
因为编译器默认创建的是一个默认简单的拷贝构造函数,只进行简单的成员变量的值复制,属于浅拷贝。如果涉及到一些复杂的初始化,指针变量,分配内存,等问题,C++提供的默认构造函数无法完成此拷贝工作,甚至会导致出错。此时我们只能手工编程copy构造函数,来达到我们要实现的目的。这就是深copy。

6.构造函数的初始化列表
构造函数初始化列表作用:
 1)构造函数的初始化列表 解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数,规则:设计了构造函数就必须初始化)
 2)初始化列表 用来 给const 属性赋值
 3) 初始化列表速度比放到构造函数里面初始化变量快
 4)第三个原因是定义为引用的属性必须使用初始化列表。
语法:
 Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)  例子: B(int _b1, int _b2, int m, int n) : a1(m), a2(n), c(0)
执行顺序:
 如果组合对象有多个,按照定义顺序(定义谁先就初始化谁先), 而不是按照初始化列表的顺序。(析构函数顺序与之相反)

7.扩展:逗号表达式

1.构造函数

为什么需要构造函数?
注意:类的数据成员是不能在声明类时初始化的。

 而创建一个对象时,常常需要作某些初始化的工作,例如对数据成员赋初值。

 为了解决这个问题,C++编译器提供了构造函数(constructor)来处理对象的初始化。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。

1.构造函数定义

 1)C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数;

 2)构造函数在定义时可以有参数;

 3)没有任何返回类型的声明。

2.构造函数的调用

 自动调用:一般情况下C++编译器会自动调用构造函数

 手动调用:在一些情况下则需要手工调用构造函数

2.析构函数

1.析构函数定义及调用

 1)C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数

语法:~ClassName()

 2)析构函数没有参数也没有任何返回类型的声明

 3)析构函数在对象销毁时自动被调用

 4)析构函数调用机制

3.案例

#include <iostream>
#include<string.h>
using namespace std;
 
class Test
{
   
public:
    Test()  //无参数 构造函数
    {
   
        a = 10;  //作用完成对属性的初始化工作
        p = new char[100];
        strcpy(p, "aaaaffff");
        cout<<"我是构造函数 被执行了"<<endl;
    }
    void print()
    {
   
        cout<<p<<endl;
        cout<<a<<endl;
    }
    ~Test() //析构函数
    {
   
        if (p != NULL)
        {
   
            cout<<"p不为空所以要清除"<<endl;
            delete []p;
        }
        cout<<"我是析构函数,被调用了" <<endl;
    }
protected:
private:
    int a ;
    char *p;
};
 
//给对象搭建一个舞台,研究对象的行为
void objplay()
{
   
    //先创建的对象 后释放
    Test t1;    //直接定义,这种情况下,如果有多个构造函数,只会调用无参数构造函数
    t1.print();
 
    printf("分隔符\n");
    Test t2;
    t2.print();
}
int main()
{
   
    objplay();
    //t1和t2的作用域消失,所以会自动调用析构函数
    cout<<"hello..."<<endl;
    return 0;
}

在这里插入图片描述

4.构造函数的分类及调用

注意࿱

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值