06-析构函数和构造函数

析构函数

创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理工作,例如释放分配的内存、关闭打开的文件等。

特点

析构函数是一种特殊的成员函数,没有返回值,是在销毁对象时自动执行。构造函数的名字和类名相同,而析构函数的名字是在类名前面加一个~符号。析构函数不允许有参数 ,不可以发生重载。

构造函数

用途:

构造函数主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

析构函数主要用于对象销毁前系统自动调用,执行一些清理工作。

基本语法:

ClassName(){}

特点

  1. 不需要返回值不用写void。
  2. 构造函数名称 与 类名 相同的 。
  3. 允许有参数,可以发生函数重载。

代码示例

#include <iostream>
/**
 * 构造函数和析构函数
 */
using namespace std;

class Person{
public:
    // 构造函数和析构函数必须要写到公共权限下面
    Person(){
        cout << "Person的构造函数调用" << endl;
    }
    
    // 析构函数
    ~Person(){
        cout << "Person的析构函数调用" << endl;
    }
};

void test01(){
    // 创建对象
    Person p;

    /*
    构造函数由编译器自动调用一次,无须手动调用
    如果我们不提供构造函数,编译器也会自动提供构造函数,默认空实现.
    */

    // 析构函数 也是编译器自动调用一次
    // 如果我们不提供析构函数,编译器会提供空实现析构函数
    // 析构函数 在对象被销毁前 自动调用
}
int main() {
    cout << "Hello, World!" << endl;

    // 调用函数
    test01();
    return 0;
}
构造函数调用

构造函数的分类

1、按参数可分为:无参构造(默认构造) 、带参构造

2、按类型可分为:拷贝构造、普通构造。

代码示例

#include <iostream>
using namespace std;

// 创建学生类
class Student{
public:
    // 定义变量
    int age;

    Student(){
        cout << "Student的默认构造函数调用" << endl;
    }

    Student(int a){
        cout << "Student的有参构造函数调用" << endl;
    }

    // 拷贝构造,值传递的本质就是调用拷贝构造函数
    Student(const Student &stu){
        cout << "Student的拷贝构造函数调用" << endl;
        age = stu.age;
    }

    ~Student(){
        cout << "Student的析构函数调用" << endl;
    }
};

// 构造函数的调用
void test01(){
    // 默认构造函数的调用
    Student stu;

}

/**
 * 调用方法
 * @return
 */

void test02(){
    /**
     * 括号法
     */

     /*
     Student stu(10); // 有参构造
     stu.age = 16;

     // 调用拷贝构造函数
     Student stu1(stu);
     cout << "stu1的年龄是:"<< stu1.age << endl;
      */

    // 注意:不要利用括号法,调用默认构造函数 Student stu(); 将代码看成函数的声明而不是认为是在创建对象。

    /**
     * 显示法
     */

     /*
     Student stu3 = Student(10); // 有参构造调用
     Student stu4 = Student(stu3); // 显示法调用拷贝构造函数
     */

     // 匿名对象
     // Student(10); // 单独写Student(10),被称为匿名对象,特点:当本行执行完毕,立即释放。
     // 不要利用拷贝构造函数,初始化匿名对象。

     // 隐式转换法 可读性低
     Student stu5 = 10; // 编译器隐式将代码转换为 Student stu = Student(10);
     // 利用隐式转换法,调用拷贝构造函数
     Student stu6 = stu5; // 隐式转为student stu6 = Student(stu5);

}
int main() {
    cout << "Hello, World!" << endl;
    // 调用函数
    // test01();

    test02();
    return 0;
}
拷贝构造函数的调用时机

方式一:

对象以值传递的方式传给函数参数

方式二:

函数局部对象以值传递的方式从函数返回.

方式三:

用一个对象初始化另一个对象

代码示例

#include <iostream>
/**
 * 拷贝构造函数的调用时机
 */
using namespace std;

// 创建学生类
class Student{
public:
    // 定义变量
    int age;

    Student(){
        cout << "Student的默认构造函数调用" << endl;
    }

    Student(int a){
        cout << "Student的有参构造函数调用" << endl;
    }

    // 拷贝构造,值传递的本质就是调用拷贝构造函数
    Student(const Student &stu){
        cout << "Student的拷贝构造函数调用" << endl;
        age = stu.age;
    }

    ~Student(){
        cout << "Student的析构函数调用" << endl;
    }
};

// 1.用已经创建好的对象初始化新的对象
void test01(){
    Student stu;
    stu.age = 19;

    // 拷贝构造函数调用
    Student stu2(stu);
    cout << "stu2的年龄是:" << stu2.age << endl;
}

// 2.值传递的方式 给函数参数传值
void doStudy(Student stu){}

void test02(){
    // 创建对象
    Student stu;
    doStudy(stu);
}

// 3、以值的方式返回局部变量
Student doStudy2(){
    Student stu;
    return stu;
}

void test03(){
    Student stu = doStudy2();
}

int main() {
    cout << "Hello, World!" << endl;
    // 调用函数
    // test01();

    // test02();
    test03();
    return 0;
}
构造函数调用规则

默认情况下,c++编译器至少为我们写的类增加3个函数。

1.默认构造函数(无参,函数体为空)。

2.默认析构函数(无参,函数体为空)。

3.默认拷贝构造函数,对类中非静态成员属性简单值拷贝。

注意点:

1、如果定义拷贝构造函数,c++不会再提供任何默认构造函数。
2、如果定义了普通构造(非拷贝),c++不在提供默认无参构造,但是会提供默认拷贝构造。

深拷贝和浅拷贝

浅拷贝

定义:同一类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是独立的两个对象,这种情况被称为浅拷贝。

一般情况下,浅拷贝没有任何副作用,但是**当类中有指针,并且指针指向动态分配的内存空间,析构函数做了动态内存释放的处理,会导致内存问题**。

深拷贝

当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要自定义拷贝构造函数,自行给指针动态分配空间。

代码示例

#include <iostream>
#include <cstring>
/**
 * 深拷贝与浅拷贝
 */
using namespace std;

// 创建Person类
class Person{
public:
    // 定义成员变量
    char * myName;
    int myAge;

    // 代参构造
    Person(char * name, int age){
        // 给Person的name,age开辟堆空间
        myName = (char *)malloc(strlen(name) + 1);
        strcpy(myName, name);
        myAge = age;
    }

    //自己提供拷贝构造函数,实现深拷贝
    Person(const Person &p){
        myAge = p.myAge;
        myName = (char *)malloc(strlen(p.myName) + 1);
        // 复制
        strcpy(myName, p.myName);
    }

    // 析构函数
    ~Person(){ // 释放在堆空间开辟的属性
        cout << "Person析构函数被调用" << endl;
        // 条件判断
        if(myName != NULL){
            free(myName);
            myName = NULL;
        }
    }
};

// 声明函数
void test01(){
    Person p1("curry", 10);
    cout << "p1的姓名:" << p1.myName << "年龄:"<<p1.myAge <<endl;

    // 调用拷贝构造函数
    Person p2 = p1; // 初始化p2对象
    cout << "p2的姓名:" << p2.myName << "年龄:" << p2.myAge << endl;
}
int main() {
    cout << "Hello, World!" << endl;
    test01();
    return 0;
}

explicit关键字

c++提供了关键字explicit,禁止通过构造函数进行的隐式转换。声明为explicit的构造函数不能在隐式转换中使用。

explicit注意

  • explicit用于修饰构造函数,防止隐式转化。
  • 是针对单参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造)而言。

代码示例

#include <iostream>
/**
 * explicit关键字的意义
 */
using namespace std;
 class MyString{
 public:
     explicit MyString(int len){
         cout << "MyString有参构造函数(int )调用" << endl;
     }
     MyString(char * str){
         cout << "MyString有参构造函数(char *)调用" << endl;
     }
 };

 // 调用函数
 void test(){
     MyString str = "kobe is mvp";
     MyString str1 = MyString("curry is child");

     MyString str2 = MyString(10);
 }
int main() {
    // 调用函数
    test();
    return 0;
}

代码运行结果

MyString有参构造函数(char *)调用
MyString有参构造函数(char *)调用
MyString有参构造函数(int )调用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值