C++ 类和对象(二)构造和析构

C++ 类和对象(二) 构造和析构


• 生活中买电子产品都会有出厂设置,在某一天我们不用的时候会删除自己的信息保证安全
• C++中的面向对象来源于生活,类似的每个对象也会有初始设置以及对象销毁前的清理数据设置

1. 构造函数和析构函数

构造: 初始化
析构: 清理

一个变量或者对象没有初始化,对其使用后果是未知的
同样,使用完一个对象或者变量,没有及时清理,也会有一定的安全问题

C++中使用构造函数和析构函数来解决上面的问题。

这两个函数会被编译器自动调用,完成初始化对象和清理的工作。
对象的初始化和清理工作是编译器强制要求我们做的事情,因此,就算我们没有写构造析构,编译器会提供他们自己的构造和析构函数,只不过这两个函数的函数体是空的。

1.1 构造和析构的作用:

• 构造函数:主要用于在创建对象时候为成员对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用
• 析构函数:主要用在对象销毁前系统自动调用,执行一些清理工作,释放内存。

1.2 构造函数语法:

类名(可以有参数)
{
}
  1. 没有返回值,没有返回类型,也不写void
  2. 函数名称和类名一样
  3. 构造函数可以有参数,因此可以发生重载
  4. 程序在调用对象时候会自动调用构造,无需手动调用,而且只调用一次(每次手机开机不需要进行初始化设置)

1.3 析构函数语法:

~类名(不能有参数)
{
}
  1. 没有返回值,不写void
  2. 函数名和类名一样,在名称前要加上符号 ~
  3. 析构函数不能有参数,不能发生重载
  4. 程序在对象销毁前会自动调用析构函数,无需手动调用,只调用一次

示例:

#include <iostream>
using namespace std;
class Person {
public:
    //构造函数
    Person() {
        cout << "调用构造函数" << endl;
    }
    //析构函数
    ~Person() {
        cout << "调用析构函数" << endl;
    }
};
void func()
{
    Person p; //函数在栈区,调用完自动释放
}
int main()
{
    func();
    system("pause");
    return 0;
}

2. 函数的分类和调用

2.1 两种分类方式

构造函数发生重载后的分类

  • 按参数分类: 有参构造和无参(默认构造)构造
  • 按类型分类: 普通构造和拷贝构造(除拷贝构造之外的其他的都叫普通构造)

拷贝构造函数:

复制一个一摸一样的Person对象出来,
就要把传进来的对象的属性都复制给当前对象
传进来的对象不能更改 , 所以加了const

固定格式:
Person(const Person &p)

  • 相同类型的对象是通过拷贝构造函数来实现复制的
  • 函数名称必须跟本类相同,且是参数是本类的引用常量

2.2 三种调用方式

  1. 括号法
  2. 显示法
  3. 隐式转换法

注意:

  1. 调用默认构造函数时候,不要加()
    因为会把它当作函数声明的,而不会创建对象了
    int func(); //这是一个函数声明 返回类型 + 函数名 + 参数
    Person p(); // 类似的 Person 是返回类型, p 是函数名
  2. 不要用拷贝构造函数来初始化一个匿名对象
    编译器认为 Person(p3) 等价于 Person p3; 而前面p3 已经声明过了

示例:

#include <iostream>
using namespace std;
class Person {
public:
    //构造函数
    Person() {
        cout << "---1---调用无参构造函数" << endl;
    }
    Person(int a) {
        m_age = a;
        cout << "---2---调用有参构造函数" << endl;
    }
    Person(const Person& p)
    {
        m_age = p.m_age;
        cout << "---3---调用拷贝构造函数" << endl;
    }
    //析构函数
    ~Person() {
        cout << "调用析构函数" << endl;
    }
    int m_age;
};
void func()
{
    //括号调用
    //Person p; //默认构造
    //Person p1(10);//有参构造
    //Person p2(p1); //拷贝构造
    //显示调用
    Person p3 = Person(10);
    //Person p4 = Person(p3);
    //Person(10);//匿名对象,执行完当前行就自动销毁
    //cout << "aaaaaaaaaaaaa" << endl;
    //不要用拷贝函数初始化一个匿名对象,编译器和i认为是对象的声明
    //Person(p3);
    //隐式调用
    //Person p5 = 10; // Person p5 = Person(10)
    //Person p6 = p5;
}
int main()
{
    func();
    system("pause");
    return 0;
}

3. 拷贝构造函

构造函数的调用时机

C++中拷贝构造函数调用时机的三种情况:

  1. 使用一个已经创建完毕的对象来初始化一个新对象
  2. 值传递的方式给参数传值
  3. 以值方式返回局部对象
//1. 使用一个已经创建完毕的对象来初始化一个新对象
void func()
{
    Person p1(10);
    Person p2(p1); 
}
// 2. 值传递
void func0(Person p) 
{
}
void func1()
{
    Person p3(10);
    func0(p3);
}
//3. 值方式返回对象
Person func2()
{
    Person p(10);
    cout << (int*)&p << endl;
    return p;
}
int main()
{
    cout << "-----使用已经创建完毕的对象来初始化对象" << endl;
    func();
    cout << "-----使用值传递的方式来初始化对象" << endl;
    func1();
    cout << "-----使用值方式返回对象" << endl;
    Person p = func2();
    cout << (int*)&p << endl;
    system("pause");
    return 0;
}

4. 构造函数调用规则

默认情况下,C++编译器至少给一个类添加3个函数

  1. 默认构造函数(无参, 函数体为空)
  2. 默认析构函数(无参,函数体为空)
  3. 默认拷贝构造函数, 对属性进行值拷贝

构造函数调用规则:
• 如果用户定义有参构造函数, C++不再提供默认无参构造,但是会提供默认拷贝构造
• 如果用户定义了拷贝构造函数, C++不会再提供其他构造函数
提供下面的就不提供上面的了
示例:

#include <iostream>
using namespace std;
class Person {
public:
    //Person() {
    //  cout << "调用默认构造函数" << endl;
    //}
    //Person(int a) {
    //  m_age = a;
    //  cout << "调用有参构造函数" << endl;
    //}
    Person(const Person& p)
    {
        m_age = p.m_age;
        cout << "调用拷贝构造函数" << endl;
    }
    //析构函数
    ~Person() {
        cout << "调用析构函数" << endl;
    }
    int m_age;
};
void func()
{
    Person p1(20);
    //p1.m_age = 20;
    Person p2(p1); 
    cout << "p2的年龄是: " << p2.m_age << endl;
}
int main()
{
    func();
    system("pause");
    return 0;
}

5. 深拷贝和浅拷贝

浅拷贝: 简单的赋值拷贝操作(编译器提供的)

问题是: 堆区的内存重复释放

深拷贝: 在堆区重新申请空间,进行拷贝操作
堆区的内存已经释放干净了,再释放就是非法操作

解决办法: 堆区的存放数据是一样的,但是指向堆区的位置不一样

注意点: 如果有属性是在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

示例:

class Person {
public:
    Person() {
        cout << "调用默认构造函数" << endl;
    }
    Person(int a, int height) {
        m_age = a;
        m_height = new int(height);//在堆区开辟了一段内存存放数据
        
        cout << "调用有参构造函数" << endl;
    }
    Person(const Person& p)
    {
        m_age = p.m_age;
        //m_height = p.m_height;//编译器的默认拷贝函数, 简单的值拷贝
        m_height = new int(*p.m_height);//深拷贝
        cout << "调用拷贝构造函数" << endl;
    }
    //析构函数
    ~Person() {
        //将堆区开辟的数据做释放操作
        //删除内存,安全
        if (m_height != NULL)
        {
            delete m_height;
            m_height = NULL;//防止野指针出现,置空
        }
        cout << "调用析构函数" << endl;
    }
    int m_age;
    int* m_height;
};
void func()
{
    Person p1(10, 180);
    cout << "---p1的年龄是: " << p1.m_age << "身高是: " << *p1.m_height << endl;
    Person p2(p1); 
    cout << "---p2的年龄是: " << p2.m_age << "身高是: " << *p2.m_height << endl;
}
int main()
{
    func();
    system("pause");
    return 0;
}

规则1 : 用户定义了有参构造函数,C++编译器就不会提供默认构造函数了
在这里插入图片描述
但是会提供默认拷贝构造函数,用于值拷贝

规则2: 用户定义了拷贝构造函数,C++编译器就不再提供默认构造函数和有参构造函数
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值