美文网首页
C++ 类和对象(二)(5)

C++ 类和对象(二)(5)

作者: maskerII | 来源:发表于2022-06-19 20:19 被阅读0次

1. 构造和析构

当对象产生时,必须初始化成员变量,当对象销毁前,必须清理对象

1.1 构造函数和析构函数的概念

初始化对象用构造函数,清理对象用析构函数,这是编译器调用的

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

构造函数语法:

  • 构造函数函数名和类名相同,没有返回值,不能有void,但可以有参数。
  • ClassName(){}

析构函数语法:

  • 析构函数函数名是在类名前面加”~”组成,没有返回值,不能有void,不能有参数,不能重载。
  • ~ClassName(){}
class Maker
{
public:
    int a;

public:
    // 构造函数的作用是初始化成员变量,是编译器去调用
    Maker()
    {
        a = 10;
        cout << "构造函数" << endl;
    }

    // 析构函数,在对象销毁前,编译器会调用析构函数
    ~Maker()
    {
        cout << "析构函数" << endl;
    }
};

void test01()
{
    // 实例化对象,内部做了两件事,1.分配空间 2.调用构造函数初始化变量
    Maker m;
    int b = m.a;
    cout << b << endl;
}

1.2 析构函数的作用

重写析构函数可以在对象释放前,手动释放对象中申请的空间,避免内存泄漏

class Maker2
{
public:
    Maker2(const char *name, int age)
    {
        cout << "有参构造" << endl;
        // 从堆区空间申请
        // +1 ,后面有一位\0
        pName = (char *)malloc(strlen(name) + 1);
        // 写入数据
        strcpy(pName, name);

        mAge = age;
    }

    void printMaker2()
    {
        cout << " name = " << pName << " age = " << mAge << endl;
    }

    ~Maker2()
    {
        cout << "析构函数" << endl;
        // 释放堆区空间
        if (pName != NULL)
        {
            free(pName);
            pName = NULL;
        }
    }

private:
    char *pName;
    int mAge;
};

void test02()
{
    Maker2 m2("Emily", 18);
    m2.printMaker2();
}

1.3 构造函数和析构函数注意事项

  • 1. 构造函数可以重载
  • 2. 构造函数必须是公有的
  • 3. 构造函数没有返回值,不能用void,构造函数可以有参数,析构函数没有返回值,不能用void,也没有参数
  • 4.有对象产生必然会调用构造函数,有对象销毁必然会调用析构函数。有多少个对象产生就会调用多少次构造函数,有多少个对象销毁就会调用多少次析构函数。
class Maker3
{
private:
    int age;

public: // 注意2:构造函数必须是公有的
    // 注意1:构造函数可以重载
    Maker3() // 无参构造函数
    {
        cout << "Maker3的无参构造" << endl;
    }
    Maker3(int a) // 有参构造函数
    {
        cout << "Maker3的有参构造" << endl;
    }
    ~Maker3()
    {
        cout << "Maker3的析构函数" << endl;
    }
};

void test03() {
    Maker3 m1; // 当构造函数私有时,实例化不了对象
    // 有对象产生必然会调用构造函数,有对象销毁必然会调用析构函数
    // 有多少个对象产生就会调用多少次构造函数,有多少个对象销毁就会调用多少次析构函数,

    Maker3 m2(10);
}

1.4 默认的构造函数和析构函数

编译器默认提供默认的构造函数和析构函数

class Maker
{
private:
    int a;
public:
    
    // 编译器提供默认的构造函数和析构函数
    // 默认的构造函数和析构函数,函数体都是空的
    void printMaker() {
        a = 100;
        cout << " a = " << a << endl;
    }
};

// 对象销毁前会去调用析构函数,对象会在test()函数结束时销毁
void test() {
    Maker a;
    a.printMaker();
}

2. 拷贝构造函数

2.1 什么是拷贝构造函数

拷贝构造函数(复制构造函数),使用另一个对象初始化本对象
ClassName(const ClassName& m){}

// 1. 什么是拷贝构造函数
class Maker1
{
public:
    Maker1()
    {
        a = 20;
        cout << "Maker1 无参构造函数" << endl;
    }
    Maker1(const Maker1 &m)
    {
        cout << "Maker1 拷贝构造函数" << endl;
        a = m.a;
    }

    void printMaker1()
    {
        cout << "Maker1 Print a " << a << endl;
    }

    ~Maker1()
    {
        cout << "Maker1 析构函数" << endl;
    }

private:
    int a;
};

void test01()
{
    Maker1 m1;
    m1.printMaker1();
    // 用一个已有的对象去初始化另一个对象
    Maker1 m2(m1);
    m2.printMaker1();
}

2.2 默认的拷贝构造函数

编译器提供了默认的拷贝构造函数,默认拷贝函数进行了成员变量的简单拷贝

// 2.编译器提供了默认的拷贝构造函数
class Maker2
{
public:
    Maker2()
    {
        a = 20;
        cout << "Maker2 无参构造函数" << endl;
    }
    // 编译器提供了默认的拷贝构造函数
    // 默认拷贝函数进行了成员变量的简单拷贝
    // Maker2(const Maker2 &m)
    // {
    //     cout << "Maker2 拷贝构造函数" << endl;
    //     a = m.a;
    // }

    void printMaker2()
    {
        cout << "Maker2 Print a " << a << endl;
    }

    ~Maker2()
    {
        cout << "Maker2 析构函数" << endl;
    }

private:
    int a;
};

void test02()
{
    Maker2 m1;
    m1.printMaker2();
    // 用一个已有的对象去初始化另一个对象
    Maker2 m2(m1);
    m2.printMaker2();
}

2.3 拷贝构造函数中形参要用引用

class Maker3
{
public:
    Maker3(int Ma)
    {
        cout << "有参构造函数" << endl;
        ma = Ma;
    }
    Maker3(const Maker3 &m)
    {
        cout << "拷贝构造函数" << endl;
    }
private:
    int ma;
};

void test03()
{
    Maker3 m1(10);//调用有参构造

    Maker3 m2(m1);

    //如果拷贝构造函数中的形参不是引用
    /*
    Maker3(const Maker3 m)//const Maker3 m=m1;   const Maker3 m(m1);
    {
        cout << "拷贝构造函数" << endl;
    }

    1.Maker3 m2(m1);
    2.const Maker3 m=m1;
    3.const Maker3 m(m1);
    4.const Maker3 m=m1;
    5.进入死循环
    */
}

3. 构造函数分类及调用

构造函数的分类

  • 按参数类型:分为无参构造函数和有参构造函数
  • 按类型分类:普通构造函数和拷贝构造函数(复制构造函数)

类默认提供了哪些函数
默认的构造函数 默认的析构函数 默认拷贝构造函数 默认的赋值函数


class Maker1
{
public:
    // 按照参数分类:无参 有参
    Maker1() {
        cout << "无参构造函数" << endl;
    }
    Maker1(int a)
    {
        cout << "有参构造函数" << endl;
        ma = a;
    }
    // 按照类型:普通构造 拷贝构造
    Maker1(const Maker1 &m)
    {
        cout << "拷贝构造函数" << endl;
    }

    // 默认的赋值函数

public:
    int ma;
};

void test01()
{
    // 常用构造函数调用
    Maker1 m; // 调用无参构造
    Maker1 m1(10);  // 调用有参构造
    Maker1 m2(m1);  // 调用拷贝构造
   
    // 不常用构造函数调用
    Maker1 m3 = m1; // 调用拷贝构造
    Maker1 m4 = 10; // 调用有参构造 Maker1 m4 = Maker1(10)
    Maker1 m5 = Maker1(10); // 调用有参构造
    Maker1 m6 = Maker1(); // 调用无参构造
    Maker1 m7(Maker1(10)); // 有参构造函数
    
    Maker1 m8; // 无参构造
    m8 = m5; // 赋值操作

    //cout << m2.ma << endl;

}

注意:不能调用拷贝构造函数去初始化匿名对象

// 不能调用拷贝构造函数去初始化匿名对象
// 不能调用拷贝构造函数去初始化匿名对象
class Teacher{
public:
    Teacher(){
        cout << "默认构造函数!" << endl;
    }
    Teacher(const Teacher& teacher){
        cout << "拷贝构造函数!" << endl;
    }
public:
    int mAge;
};
void test02()
{
    Teacher t1; // 默认构造函数
    // error C2086:“Teacher t1”: 重定义
    // Teacher(t1);  //此时等价于 Teacher t1;

    Teacher(t2); // 此时等价于 Teacher t2 默认构造函数

    Teacher t3 = Teacher(t2); // 拷贝构造函数
}

b为A的实例化对象,A a = A(b) 和 A(b)的区别?
当A(b) 有变量来接的时候,那么编译器认为他是一个匿名对象,当没有变量来接的时候,编译器认为A(b) 等价于 A b.

4. 匿名对象

匿名对象的生命周期在当前行

// 匿名对象的生命周期在当前行
// 如果匿名对象有名字来接,那么就不是匿名对象 Maker m1 = Maker();
class Maker
{
    public:
    Maker() {
        cout << "无参构造函数" << endl;
    }

    Maker(int a){
        cout << "有参构造函数" << endl;
    }
    Maker(const Maker &m){
        cout << "拷贝构造函数" << endl;
    }
    ~Maker() {
        cout << "析构函数" << endl;
    }
private:
   

};

void test01() 
{
    Maker(); // 匿名对象的生命周期在当前行
    Maker(10);
    // 注意:如果匿名对象有名字来接,那么就不是匿名对象
    Maker m1 = Maker();
    cout << "test01()函数结束" << endl;
}

5. 拷贝构造函数调用的时机

5.1 对象以值方式给函数参数

// 1. 对象以值方式给函数参数
void func(Maker m){

}

void test01() {
    Maker m1; // 无参构造
    func(m1); // 拷贝构造 Maker m = m1 
}

5.2 用一个已有的对象初始化另一个对象

// 2.用一个已有的对象初始化另一个对象
void test02() {
    Maker m;
    Maker m1(m);
}

5.3 函数的局部对象以值的方式从函数返回

// 3. 函数的局部对象以值的方式从函数返回
// vs Debug模式下,会调用拷贝构造, vs Release模式下不会调用拷贝构造,qt也不会调用 (节约内存)
Maker func2() {
    // 局部对象
    Maker m(10);
    cout << "局部对象的地址 " << &m << endl;
    return m;
}
void test03() {
    // Maker m1; 执行到;时,对象才产生
    // Maker m1 =  此时对象还未产生
    Maker m1 = func2();
    cout << "m1对象的地址 " << &m1 << endl;
}

[Test03结果说明:]
编译器存在一种对返回值的优化技术,RVO(Return Value Optimization).在vs debug模式下并没有进行这种优化,所以函数MyBusiness中创建p对象,调用了一次构造函数,当编译器发现你要返回这个局部的对象时,编译器通过调用拷贝构造生成一个临时Person对象返回,然后调用p的析构函数。
我们从常理来分析的话,这个匿名对象和这个局部的p对象是相同的两个对象,那么如果能直接返回p对象,就会省去一个拷贝构造和一个析构函数的开销,在程序中一个对象的拷贝也是非常耗时的,如果减少这种拷贝和析构的次数,那么从另一个角度来说,也是编译器对程序执行效率上进行了优化。

6. 构造函数调用规则

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

如果程序员提供了有参构造,那么编译器不会提供默认构造函数,但是会提供默认的拷贝构造

class Maker
{
public:
    Maker(int a){
        cout << "有参构造函数" << endl;
    }
    ~Maker() {
        cout << "析构函数" << endl;
    }
private:
   
};
// 如果程序提供了有参构造函数,那么编译器就不会提供默认的构造函数,但会提供默认的拷贝构造函数
void test01() 
{
    // Maker m; // 报错
    Maker m1(10);
    Maker m2(m1); 
}

如果程序员提供了拷贝构造函数,那么编译器不会提供默认的构造函数和默认的拷贝构造函数

class Maker2
{
public:
    Maker2(const Maker &m)
    {
        cout << "拷贝构造函数" << endl;
    }
    ~Maker2()
    {
        cout << "析构函数" << endl;
    }

private:
};
// 如果程序提供了拷贝构造函数,那么编译器就不会提供默认的构造函数和默认的拷贝构造函数
void test02()
{
    // Maker2 m; // 错误
}

相关文章

  • C++ 类和对象(二)(5)

    1. 构造和析构 当对象产生时,必须初始化成员变量,当对象销毁前,必须清理对象 1.1 构造函数和析构函数的概念 ...

  • C++零基础教程之类和对象初识

    C++ 类和对象 C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核...

  • C++面向对象

    C++类和对象 C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心...

  • Boolan C++面向对象高级编程(上)第一周

    一、C++编程简介 C++ 98(1.0) C++ 11(2.0) 二、头文件与类的说明 1.基于对象与面向对象 ...

  • [C++之旅] 7 C++类和对象

    [C++之旅] 7 C++类和对象 类和对象 类:相当于一个种类,以狗狗为例,狗就是一个类,狗这个类里包含无数种不...

  • C++ — 类 & 对象超详解

    C++ 类 & 对象 C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ ...

  • C++系列之面向对象

    C++类&对象 C++类定义 类&对象详解 概念描述类成员函数[https://www.runoob.com/cp...

  • 一、类和对象

    *************************C++ 第一天 类和对象********************...

  • 目录

    tips:一下内容都是C语言中文网的内容,我只是摘取我需要的知识 二.C++类和对象 (1)C++对象的内存模...

  • C++面向对象基础(二)

    目录 C++ 基础(一) C++面向对象基础(二) 一、类 C++ 中可以使用 struct、class 来定义一...

网友评论

      本文标题:C++ 类和对象(二)(5)

      本文链接:https://www.haomeiwen.com/subject/vvarvrtx.html