构造析构

正文

构造函数

为成员变量赋初值,分配资源,设置对象的初始状态 可以理解为类的初始化函数

构造函数的使用

#include<iostream>
using namespace std;
class STU
{
    char* m_name;
    int m_age;
public:
    //STU(){} 缺省构造函数 无参 一旦你实现了任意一个构造函数将不再提供缺省构造
    STU()
    {
        m_name = new char[20]{ "学生姓名" };//分配内存并初始化
        m_age = 18;
    }
    void introduce()
    {
        cout << "name:" << m_name << endl << "age:" << m_age << endl;
    }
};
int main()
{
    STU stu;//自动调用构造函数
    stu.introduce();
    return 0;
}

构造函数一般用于为对象分配内存空间

特点

  • 函数名与类名相同,没有返回类型
  • 对象创建时自动调用且只调用一次
  • 如果没有定义构造函数,那么编译器会自动生成一个默认的构造函数,只是这个构造函数的函数体是空的,也没有形参,也不执行任何操作
  • 一般访问属性为public,除非我们不允许外部创建对象

构造函数的重载

和普通成员函数一样,构造函数是允许重载的。一个类可以有多个重载的构造函数,创建对象时根据传递的实参来判断调用哪一个构造函数

#include<iostream>
using namespace std;
class STU
{
    char* m_name;
    int m_age;
public:
    STU()
    {
        m_name = new char[20]{ "学生姓名" };//分配内存并初始化
        m_age = 18;
    }
    //构造函数的重载
    STU(const char* name, int age)
    {
        m_name = new char[20];//分配了内存就要释放
        strcpy(m_name, name);
        m_age = age;
    }
    void introduce()
    {
        cout << "name:" << m_name << endl << "age:" << m_age << endl;
    }
};
int main()
{
    STU stu;              //自动调用构造函数
    stu.introduce();
    STU stu1("程序", 20); //自动匹配并调用构造函数的重载
    stu1.introduce();
    return 0;
}
//打印结果
name:学生姓名
age:18
name:程序
age:20

析构函数

创建对象时系统会自动调用构造函数进行初始化工作,对应的,销毁对象时系统也会自动调用一个函数来进行清理工作

析构函数的使用

//在构造代码下面追加析构函数
~STU()
{
    delete[] m_name;    //构造申请内存,析构释放内存
}

销毁对象时系统自动调用析构函数

特点

  • 构造函数的名字和类名相同,而析构函数的名字是在类名前面加一个~符号
  • 对象销毁时自动调用且只调用一次
  • 如果用户没有定义,编译器会自动生成一个默认的空的析构函数
  • 析构函数没有参数,不能被重载,因此一个类只能有一个析构函数

关于delete[]

为什么释放多个内存要加[]

为了测试这一情况,定义一个类

class test
{
public:
    test()
    {
        cout << "构造" << endl;
    }
    ~test()
    {
        cout << "析构" << endl;
    }
};

在主函数中申请多个对象后加[]释放

int main()
{
    test *pTest = new test[4];
    delete[] pTest;
    return 0;
}
输出结果:
构造
构造
构造
构造
析构
析构
析构
析构

不加[]释放

int main()
{
    test *pTest = new test[4];
    delete pTest;
    return 0;
}
输出结果:
构造
构造
构造
构造
析构

可以看出不加[]只释放一个对象的内存,并且还会报错

其实呢,在申请多个内存是返回的内存地址并不是申请的内存首地址而是向右移4个字节之后的地址,我们可以打印出隐藏的4个字节内容

int main()
{
    test *pTest = new test[4];
    cout << *((int*)(pTest - 4)) << endl;
    delete[] pTest;
    return 0;
}
打印结果:
构造
构造
构造
构造
4
析构
析构
析构
析构

这样你会发现隐藏的4个字节存储了你申请的对象数量当delete加[]时,会先访问这4个字节的数据,然后再释放内存

构造析构顺序

在构造析构顺序之前先看一下

  • 对象创建过程(以堆区为例)
  1. 为整个对象分配内存
  2. 构造基类部分(如果存在基类)
  3. 构造成员变量
  4. 执行构造函数代码
  • 对象的销毁过程
  1. 执行析构函数代码
  2. 析构成员变量
  3. 析构基类部分
  4. 释放整个对象占用内存

这样我们先创建三个类(A,B,C),其中B类中有A对象,C类中有B对象

#include<iostream>
using namespace std;
class A
{
public:
    A(){
        cout << "A构造" << endl;
    }
    ~A() {
        cout << "A析构" << endl;
    }
};
class B
{
public:
    B() {
        cout << "B构造" << endl;
    }
    ~B() {
        cout << "B析构" << endl;
    }
    A a;
};
class C
{
public:
    C() {
        cout << "C构造" << endl;
    }
    ~C() {
        cout << "C析构" << endl;
    }
    B b;
};

int main()
{
    C* c=new C; //创建对象
    delete c;   //销毁对象
    return 0;
}
//打印结果
A构造
B构造
C构造
C析构
B析构
A析构

初始化列表

简化成员变量初始化,仅仅只是为了书写方便,没有效率上的提升

class STU
{
    string m_name;  //姓名
    int m_age;      //年龄
    int m_height;   //身高
public:
    STU():m_name("学生姓名"),m_age(18),m_height(180) { }
    STU(string name, int age,int height)
        :m_name(name)
        ,m_age(age)
        ,m_height(height) { }
    void introduce()
    {
        cout << "Name:" << m_name << "\tAge:" << m_age << "\tHeight:" << m_height << endl;
    }
};

int main()
{
    STU stu;
    stu.introduce();
    STU stu1("程序", 20,180);
    stu1.introduce();
    return 0;
}

在初始化列表中需要注意的是参数初始化顺序与初始化表列出的变量的顺序无关,它只与成员变量在类中声明的顺序有关,这个会导致什么问题呢

//将构造函数的重载修改成
STU(string name, int age,int height)
    :m_name(name)
    , m_height(height)
    ,m_age(m_height){ }
输出结果:
Name:学生姓名   Age:18  Height:180
Name:程序       Age:-858993460  Height:180

这里只是该变了初始化列表的顺序,然后用m_height成员变量去初始化m_age

造成这样后果的原因是初始化列表先初始化m_age,再初始化m_height

本文分享自微信公众号 - 编程学习基地(LearnBase),作者:DeRoy

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-02-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C到C++

    DeROy
  • 预处理

    这些宏定义不仅可以帮助我们完成跨平台的源码编写,灵活使用也可以巧妙地帮我们输出非常有用的调试信息

    DeROy
  • Qt项目网络聊天室设计

    3. 服务器接收到某个客户端的请求以及发送信息,经服务器发给其它客户端 最终实现一个共享聊天内容的聊天室!

    DeROy
  • C++之类和对象的使用(一)

      对象的初始化 在声明类时直接对数据成员初始化是错误的!下面的例子时错误的!! class Time{ hour =0; minitu=0; sec=0; }...

    互联网金融打杂
  • 重拾Kotlin(19)-中缀调用、解构声明

    使用 “to” 来声明 map 的 key 与 value 之间的对应关系,这种形式的函数调用被称为中缀调用

    叶应是叶
  • 机器学习(十九)EM:期望最大算法

    最大期望算法(Expectation Maximization Algorithm,又译期望最大化算法),是一种迭代算法,用于含有隐变量(hidden vari...

    致Great
  • [ecshop模板]如何清除测试数据

    Ecshop的后台是很强大的,我们只需要把ecshop的所有测试商品删除就可以了。请看详细教程: 1、删除ecshop测试商品数据 在登入后台,找到商品列表,勾...

    96php.cn
  • 【独家重磅】来自华尔街的量化金融面试Q&A(第三期)

    量化投资与机器学习微信公众号将定期推送至少200期以上的华尔街量化金融面试Q&A。所有题目均来自国外高质量的面试宝典,我们做了精心的翻译和解读。这些面试题目涉及...

    量化投资与机器学习微信公众号
  • 硬币与计算机中的“数据”

    最近与几个朋友聊到了“数据的本质”相关的话题,惊讶地发现,即使是计算机相关的专业,许多朋友也没搞清楚”数据究竟是怎么一回事“这个问题。

    zgq354
  • 子弹跟踪效果

    打击的目标一直在移动,但是子弹却像长了眼睛一样在后面尾随,直到精准击中目标。这种“长了眼睛的子弹”,是打击类游戏中比较经典的武器之一。

    异名

扫码关注云+社区

领取腾讯云代金券