C++核心编程之类和对象---对象特性--- 构造函数和析构函数(对象的初始化和清理)

目录

一、构造函数和析构函数(对象的初始化和清理)

作用

功能实现

二、构造函数

语法:类名 () {}

三、析构函数

语法  ~类名(){}

四、构造函数的分类及调用

五、拷贝构造函数的调用时机

六、构造函数调用规则

七、深拷贝与浅拷贝的问题        

八、初始化列表

九、类对象作为类成员

十、静态成员

1.静态成员分类

2.静态成员变量

3.静态成员函数


一、构造函数和析构函数(对象的初始化和清理)

对象的初始化和清理

        生活中我们买的电子产品都基本会有出厂设罩,在某一天我们不用时候也会删除一些自己信息数据保证安全

        C++中的面向对象来源于生活,每个对象也都会有初始设置以及对象销毁前的清理数据的设置。

作用

        一个对象或者变量没有初始状态,对其使用后果是未知的,

        同样的使用完一个对象或变量没有及时清理,也会造成一定的安全问题

功能实现

        C++利用构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作对象的初始化和清理工作是编译器强制我们要做的事情,因此如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现。

        

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

 

二、构造函数

语法:类名 () {}

  • 构造函数,没有返回值也不写void
  • 函数名称与类名相同
  • 构造函数可以有参数,因此可以发生重载
  • 程序调用对象的时候会自动调用构造函数,无须手动调用,而且只会调用一次

示例:

#include

using namespace std;

class person{

public:// 在公共作用域下在主函数才能访问到

    // 构造函数

person()

    {

        // 在创建一个对象的时候会自动调用一次

        cout<<"person 构造函数的调用"<

运行结果:

 

三、析构函数

语法  ~类名(){}

  • 析构函数,没有返回值也不写void
  • 函数名称与类名相同称
  • 析构函数不可以有参数,因此不可以发生重载
  • 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次

示例:

#include

using namespace std;

class person{

public:// 在公共作用域下在主函数才能访问到

    // 析构函数

    ~person ()

    {

        cout<<"析构函数的调用"<

运行结果:

四、构造函数的分类及调用

两种分类方式:

  •         按参数分为:有参构造和无参构造
  •         按类型分为:普通构造和拷贝构造

三种调用方式:

  •         括号法
    •         显示法
      •         隐式转换法

示例:

#include

using namespace std;

class person {

public:

    // 构造函数

    person() // 无参

    {

        cout << "person 的无参数的构造函数调用。" << endl;

    }

    person(int a) // 有参

    {

        age = a;

        cout << "person 的有参数的构造函数调用。" << endl;

    }



    // 拷贝构造函数

    person(const person& p)

    {

        // 把另一个person的属性传入进来 +const 防止改变原来的属性

        // 将传入的人身上的所有的属性全部拷贝到自己的身上

        age = p.age;

        cout << "person 的拷贝构造函数调用。" << endl;

    }



    // 析构函数

    ~person()

    {

        cout << "person 的析构函数的调用。" << endl;

    }

    int age;

};

void test()

{

    // 调用

    // 1. 括号法

    cout << "括号法调用" << endl;

    person p1; // 默认构造函数调用

    // 注意事项1:默认构造函数的调用不要加()  因为会被认为是一个函数的声明,不会认为在创建对象

    person p2(10); // 调用有参构造函数

    // 拷贝构造函数的调用

    person p3(p2); // 拷贝构造函数的调用



    cout << "p2的年龄:" << p2.age << endl;

    cout << "p3的年龄:" << p3.age << endl;

    cout << endl;



    // 2. 显示法

    cout << "显示法调用" << endl;

    person p4; // 默认构造函数

    person p5 = person(10); // 有参构造

    person p6 = person(p3);  // 拷贝构造

    // 右侧相当于匿名对象,当执行结束后系统会立即回收掉匿名对象
    // 注意事项2:不要利用拷贝构造函数 初始化 匿名对象
    // 编译器会认为  person(p3)  === person p3,相当于时对象的声明

    cout << endl;



    cout << "测试匿名对象" << endl;

    person(10);

    cout << "aaa" << endl << endl;



    // 3. 隐式转换法

    cout << "隐式转换法调用" << endl;

    person p7 = 10; // 相当于person p3 = person(10);是一种有参构造

    person p8 = p7;

    cout << endl;

   }

int main()

{

    test();

    return 0;

    system("pause");

}

运行结果:

C++核心编程之类和对象---对象特性--- 构造函数和析构函数(对象的初始化和清理)_第1张图片

五、拷贝构造函数的调用时机

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

  • 使用一个已经创建完毕的对象来初始化一个新对象
  • 值传递的方式给函数参数传值
  • 以值方式返回局部对象

示例:

#include
using namespace std;
// 拷贝函数的三种调用时机
class person {
public:
    person()
    {
        cout << "person 的默认函数构造" << endl;
    }
    // 有参
    person(int in_age)
    {
        age = in_age;
        cout << "有参构造函数" << endl;
    }
    // 拷贝
    person(const person& p)
    {
        age = p.age;
        cout << "拷贝构造函数" << endl;
    }
    // 析构
    ~person()
    {
        cout << "person 的析构函数的调用" << endl;
    }
//private:
    int age;
};

// 1. 使用一个已经创建完毕的对象来初始化一个新的对象
void test01()
{
    person p1(20); // 有参构造
    person p2(p1); // 拷贝构造
    cout << "p2的年龄为:" << p2.age << endl;
}

// 2.值传递的方式给函数参数传值
void do_work(person p)
{

}

void test02()
{
    person p;
    do_work(p);
}

// 3. 以值的方式返回局部对象
person do_work2()
{
    person p1;
    cout << (int*)&p1 << endl;
    return p1;  // 拷贝一个新的对象用于返回
}

void test03()
{
    person p = do_work2(); // 得到的是拷贝的返回值
    cout << (int*)&p << endl;
}

int main()
{
    test01();
    cout << endl;
    test02();
    cout << endl;
    test03();
    return 0;
}

运行结果:

C++核心编程之类和对象---对象特性--- 构造函数和析构函数(对象的初始化和清理)_第2张图片

六、构造函数调用规则

  • 默认情况下,C++编译器至少给一个类添加3个函数
  •         1. 默认构造函数(无参,函数体为空)
  •         2. 默认析构函数(无参,函数体为空)
  •         3. 默认拷贝构造函数,对属性进行值拷贝
  • 构造函数调用规则:
  •         如果写了有参构造函数,编译器就不再提供默认构造函数,但是依然提供拷贝构造函数
  •         如果写了拷贝构造函数,编译器就不再提供默认构造函数和有参构造函数

示例:

#include

using namespace std;

class person

{

public:

    // 默认

    person()

    {

        cout<<"person 的默认函数构造"<age = p.age;

        cout<<"person 的拷贝构造函数"<

运行结果:

C++核心编程之类和对象---对象特性--- 构造函数和析构函数(对象的初始化和清理)_第3张图片

 

七、深拷贝与浅拷贝的问题        

  • 浅拷贝:简单的赋值拷贝操作
  • 深拷贝:在堆区重新申请空间,进行拷贝操作

浅拷贝带来的问题是堆区的内存重复释放,利用深拷贝来解决。

示例:

#include

using namespace std;

class person

{

public:

    // 默认

    person()

    {

        cout<<"person 的默认函数构造"<age = age;

        cout<<"person 的有参构造函数"<height = new double(height); // new 一个新的指针变量,创建在堆区

    }

 

    // 拷贝

    person(const person & p)

    {

        this->age = p.age;  // 编译器默认实现

        cout<<"person 的拷贝构造函数"<age = p.height; // 编译器的 拷贝构造默认实现方式

 

        // 解决浅拷贝带来的问题

 

        // 两个对象的指针分别设立在不同的存储地址

        this->height = new double(*p.height); // 利用深拷贝来解决浅拷贝的问题

    }

 

    // 析构

    ~person()

    {

        // 将我们在堆区开辟的数据做释放操作

        if(height !=NULL){

            delete height;

            height = NULL;

        }

        cout<<"person 的析构函数的调用"<

运行结果:

C++核心编程之类和对象---对象特性--- 构造函数和析构函数(对象的初始化和清理)_第4张图片

 

八、初始化列表

作用:C++提供了初始化列表语法,用来初始化属性。

语法 : 构造函数():属性1(值1),属性2(值2)... {}

示例:

#include

using namespace std;

class person

{

public:

    // 传统初始化操作

    /*person(int a ,int b,int c)

    {

        A = a;

        B = b;

        C = c;

    }*/

 

    // 初始化列表属性,赋值默认值

    /*person():A(10),B(20),C(30)

    {

 

    }*/

 

    // 设置成变量

    person(int a,int b,int c):A(a),B(b),C(c)

    {

 

    }

 

    int A;

    int B;

    int C;

};

void test01()

{

    // 每次调用一种

    person p(10,20,30);

    cout<<"A = "<C++核心编程之类和对象---对象特性--- 构造函数和析构函数(对象的初始化和清理)_第5张图片

九、类对象作为类成员

c++类中的成员是另一个类的对象,称该成员为对象成员

例如:

class A{};

class B

{

A a;

};

B类中有对象A作为成员,A为对象成员

那么,当创建B对象时,A与B的构造和析构顺序ABBA

示例:

#include

#include

using namespace std;

// 设计一个手机类

class phone{

public:

    // 给手机命名

    // 构造函数

    phone(string in_name)

    {

        p_name = in_name;

        cout<<"这是phone的构造函数调用"<

运行结果:

C++核心编程之类和对象---对象特性--- 构造函数和析构函数(对象的初始化和清理)_第6张图片

十、静态成员

        静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员

1.静态成员分类

静态成员变量:    

  • 所有对象共享同一份数据
  • 在编译阶段分配内存
  • 类内声明,类外初始化

静态成员函数:

  • 所有对象共享同一个函数
  • 静态成员函数只能访问静态成员变量

2.静态成员变量

示例:

#include

#include

using namespace std;

class person {

public:

    static int m_A; // 静态成员变量

    // 类内声明,类外初始化



private:

    static int m_B;// 静态成员变量

    // 在类外访问不到

};

int person::m_A = 0;

int person::m_B = 200;



// 两种访问方式

void test01()

{

    // 1. 通过对象访问

    cout << "通过对象访问" << endl;

    person p1;

    cout << "p1_a = " << p1.m_A << endl;



    person p2;
    
    p2.m_A = 200;

    cout << "共享数据,p2修改了数据,p1访问就变为p2修改后内容" << endl;

    cout << "p1_a = " << p1.m_A << endl;

    cout << "p2_a = " << p2.m_A << endl;



    // 2. 通过类名访问

    cout << "通过类名访问:" << person::m_A << endl;



    //并且私有权限类外访问不到

}

int main()

{

    test01();



    return 0;

}

运行结果:

C++核心编程之类和对象---对象特性--- 构造函数和析构函数(对象的初始化和清理)_第7张图片

  

3.静态成员函数

示例:

#include

#include

using namespace std;

class person{

public:

    // 静态的成员函数

    static void func()

    {

        m_A = 100;

        // m_B = 200;  // 静态成员函数不能访问非静态成员变量

        // 静态成员函数是每个对象都共享的,调用非静态成员变量时不知道修改的是哪个对象的变量

        cout<<"静态函数的调用"<

运行结果: 

C++核心编程之类和对象---对象特性--- 构造函数和析构函数(对象的初始化和清理)_第8张图片

 

你可能感兴趣的:(#,C++核心编程,c++,开发语言)