构造函数和析构函数

对象的构造和析构

1.基本概念

  创建一个对象时,常常需要作某些初始化的工作,例如对数据成员赋初值。注意,类的数据成员是不能在声明类时初始化的。
  为了解决这个问题,C++编译器提供了构造函数(constructor)来处理对象的初始化。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。

  • 构造函数定义及调用 
    1)C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数; 
    2)构造函数在定义时可以有参数; 
    3)没有任何返回类型的声明。 

  • 构造函数的调用 
    自动调用:一般情况下C++编译器会自动调用构造函数 
    手动调用:在一些情况下则需要手工调用构造函数 

  • 析构函数定义及调用 
    1)C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数 
    语法:~ClassName() 
    2)析构函数没有参数也没有任何返回类型的声明 
    3)析构函数在对象销毁时自动被调用 
    4)析构函数调用机制 
    C++编译器自动调用

  • 设计构造函数和析构函数的原因 
      面向对象的思想是从生活中来,手机、车出厂时,是一样的。生活中存在的对象都是被初始化后才上市的;初始状态是对象普遍存在的一个状态的

  • 普通方案: 
    为每个类都提供一个public的initialize函数; 
    对象创建后立即调用initialize函数进行初始化。

  • 优缺点分析 
    1)initialize只是一个普通的函数,必须显示的调用 
    2)一旦由于失误的原因,对象没有初始化,那么结果将是不确定的,没有初始化的对象,其内部成员变量的值是不定的 
    3)不能完全解决问题

类似于下面的情况更加说明了构造函数的必要性:


#include <iostream>
using namespace std;


class Test3
{
public:
    void init(int _a, int _b)
    {
        a = _a;
        b = _b;
    }

protected:
private:
    int a;
    int b;
};


void main31()
{

    //类没有提供构造函数,c++编译器会自动给你提供一个默认的构造函数
    //类没有提供构造函数 copy构造构造函数, c++编译器会自动给程序员提供一个 默认的copy构造函数  = 
    Test3 t1; 
    int a = 10; 
    int b = 20;
    t1.init(a, b);

    Test3 tArray[3];
    tArray[0].init(1, 2);
    tArray[1].init(1, 2);
    tArray[2].init(1, 2);

    //
    Test3 t21;  t21.init(1, 2);
    Test3 t22;  t22.init(1, 2);
    Test3 t23;  t23.init(1, 2);

    //在这种场景之下 显示的初始化方案 显得很蹩脚
    Test3 tArray2[3] = {t21, t22, t23};

    //在这种场景之下,满足不了,编程需要
    Test3 tArray3[1999] = {t21, t22, t23};


    cout<<"hello..."<<endl;
    system("pause");
    return ;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

基础(有参)构造函数和析构函数示例代码

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
using namespace std;

class Test
{
public:
    Test(int a,int flag){
        m_a = a;
        obj_flag = flag;
        ptr = (char*)malloc(100);
        if (NULL == ptr)
            obj_flag = 0;
        strcpy(ptr, "abcdefg");
        cout << obj_flag<<"/tI am constructor function" << endl;
    }

    void print()
    {
        cout << m_a << endl;
        cout << ptr << endl;
        cout << obj_flag << endl;
    }

    ~Test()
    {
        if (ptr != NULL)
        {
            free(ptr);
            ptr = NULL;
        }
        cout << obj_flag << "/tI am destructor function" << endl;
    }
protected:
private:
    char *ptr;
    int m_a;
    int obj_flag;
};


//给对象搭建一个舞台,研究对象的行为
void objplay()
{
    //先创建的对象 后释放
    Test t1(10,20);
    t1.print();

    printf("分隔符\n");
    Test t2(30,40);
    t2.print();
}

int main(void)
{
    objplay();

    system("pause");
    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61

2.构造函数分类及调用规则

2.1.无参数构造函数

调用方法: Test t1, t2;

#include <iostream>
using namespace std;

class Test2
{
public:
    Test2()  //无参数构造函数
    {
        m_a = 0;
        m_b = 0;
        cout << "无参数构造函数" << endl;
    }


public:
    void printT()
    {
        cout << "普通成员函数" << endl;
    }
private:
    int m_a;
    int m_b;
};

void main()
{

    Test2 t1;  //调用无参数构造函数
    cout << "hello..." << endl;
    system("pause");
    return;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

2.2.有参数构造函数

一共三种情况调用有参构造函数

  1. 括号法:ClassName obj(int a) 
    2.等号法:ClassName obj=(3,4) 
    3.显示调用 ClassName obj=ClassName(3,4)

示例代码如下:



#include <iostream>
using namespace std;

class Test2
{
public:
    Test2()  //无参数构造函数
    {
        m_a = 0;
        m_b = 0;
        cout << "无参数构造函数" << endl;
    }

    Test2(int a)
    {
        m_a = a;
        m_b = 0;
        cout <<m_a<< "/tTest2(int a)有参数构造函数" << endl;
    }

    Test2(int a, int b) //有参数构造函数   //3种方法
    {
        m_a = a;
        m_b = b;
        cout << "Test2(int a, int b)有参数构造函数" << endl;
    }

    //赋值构造函数 (copy构造函数) //
    Test2(const Test2& obj)
    {
        cout << "copy构造函数 " << endl;
    }

public:
    void printT()
    {
        cout << "普通成员函数" << endl;
    }
private:
    int m_a;
    int m_b;
};



//调用 调用有参数构造函数 3
void main()
{
    //1括号法 
    Test2 t1(1, 2);  //调用参数构造函数  c++编译器自动的调用构造函数
    t1.printT();

    // 2 =号法
    Test2 t2 = (3, 4, 5, 6, 7); // = c++对等号符 功能增强  c++编译器自动的调用构造函数

    Test2 t3 = 5;

    //3 直接调用构造函数  手动的调用构造函数
    Test2 t4 = Test2(1, 2);   //匿名对象 (匿名对象的去和留) 抛砖 ....//t4对象的初始化
    //

    t1 = t4;  //把t4 copy给 t1  //赋值操作 
    //对象的初始化 和 对象的赋值 是两个不同的概念 

    cout << "hello..." << (3, 4, 5, 6, 7)<<endl;//逗号表达式
    system("pause");
    return;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

2.3.拷贝构造函数

  • 用1个对象去初始化另外一个对象 (等号法)
#include <iostream>
using namespace std;

class Test
{
public:
    Test()
    {
        m_a = 0;
        cout << "无参数构造函数" << endl;
    }

    Test(int a)
    {
        m_a = a;
        cout << "有参数构造函数" << endl;
    }

    Test(const Test & obj)
    {
        m_a = obj.m_a + 100;
        cout << "我也是构造函数 " << endl;
    }
    void printT()
    {

        cout << "m_a/t" << m_a << endl;
        cout << "普通成员函数" << endl;
    }
protected:
private:
    int m_a;
};

int main(void)
{
    Test t1;
    Test t0(2);

    //赋值=操作 会不会调用构造函数
    //operator=()//抛砖
    cout << "---" << endl;
    t0 = t1; //用t1 给 t0赋值  到操作 和 初始化是两个不同的概念
    cout << "---" << endl;
    //第1种调用方法
    Test t2 = t1; //用t1来初始化 t2 
    t2.printT();

    system("pause");
    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

  • 用1个对象去初始化另外一个对象 (括号法)
#include <iostream>
using namespace std;

class Test
{
public:
    Test()
    {
        m_a = 0;
        cout << "无参数构造函数" << endl;
    }

    Test(int a)
    {
        m_a = a;
        cout << "有参数构造函数" << endl;
    }

    Test(const Test & obj)
    {
        m_a = obj.m_a + 100;
        cout << "我也是构造函数 " << endl;
    }
    void printT()
    {

        cout << "m_a\t" << m_a << endl;
        cout << "普通成员函数" << endl;
    }
protected:
private:
    int m_a;
};

int main(void)
{
    Test t1;

    //第2种调用方法
    Test t2(t1); //用t1来初始化 t2 
    t2.printT();

    system("pause");
    return 0;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

  • 函数调用时,实参初始化形参(形参是一个元素不是引用)

#include <iostream>
using namespace std;


class Location
{
public:
    Location(int xx = 0, int yy = 0)
    {
        X = xx;  Y = yy;  cout << "Constructor Object.\n";
    }

    //copy构造函数  完成对象的初始化
    Location(const Location & obj) //copy构造函数 
    {
        X = obj.X; Y = obj.Y; cout << "我也是构造函数 " << endl;
    }
    ~Location()
    {
        cout << X << "," << Y << " Object destroyed." << endl;
    }
    int  GetX() { return X; }       int GetY() { return Y; }
private:   int  X, Y;
};


//业务函数  形参是一个元素
void f(Location p)
{
    cout << p.GetX() << endl;
}

void playobj()
{
    Location  a(1, 2);
    Location  b = a;
    cout << "b对象已经初始化完毕" << endl;

    f(b); //b实参初始化形参p,会调用copy构造函数
}

void main()
{
    playobj();
    cout << "hello..." << endl;
    system("pause");
    return;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

  • 当函数的返回类型是一个元素时,也就是返回一个对象



#include <iostream>
using namespace std;


class Location
{
public:
    Location(int xx = 0, int yy = 0)
    {
        X = xx;  Y = yy;  cout << "Constructor Object.\n";
    }

    //copy构造函数  完成对象的初始化
    Location(const Location & obj) //copy构造函数 
    {
        X = obj.X; Y = obj.Y; cout << "Copy Constructor Object.\n";
    }
    ~Location()
    {
        cout << X << "," << Y << " Object destroyed." << endl;
    }
    int  GetX() { return X; }       int GetY() { return Y; }
private:   int  X, Y;
};

//g函数 返回一个元素 
//结论1 : 函数的返回值是一个元素 (复杂类型的), 返回的是一个新的匿名对象(所以会调用匿名对象类的copy构造函数)

//
//结论2: 有关 匿名对象的去和留
//如果用匿名对象  初始化 另外一个同类型的对象, 匿名对象 转成有名对象
//如果用匿名对象  赋值给 另外一个同类型的对象, 匿名对象 被析构

//
//你这么写代码,设计编译器的大牛们:
//我就给你返回一个新对象(没有名字 匿名对象)
Location g()
{
    Location A(1, 2);
    return A;
}

//
void objplay2()
{
    g();
}

//
void objplay3()
{
    //用匿名对象初始化m 此时c++编译器 直接把匿名对转成m;(扶正) 从匿名转成有名字了m
    Location m = g();
    printf("匿名对象,被扶正,不会析构掉\n");
    cout << m.GetX() << endl;;
}

void objplay4()
{
    //用匿名对象 赋值给 m2后, 匿名对象被析构
    Location m2(1, 2);
    m2 = g();
    printf("因为用匿名对象=给m2, 匿名对象,被析构\n");
    cout << m2.GetX() << endl;;
}
void main()
{
    objplay2();
    objplay3();
    objplay4();
    cout << "hello..." << endl;
    system("pause");
    return;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

  • 一旦在子函数中·return obj·,就会立即调用拷贝构造函数
  • 子函数的返回值是一个匿名对象,只有当把该返回值用来初始化一个同类型的对象时,匿名对象才不会析购
  • 没有接收返回值名或者将返回值赋值给一个已经初始化过的对象,匿名对象都将被析构

  • 拷贝构造函数可以重载



#include <iostream>
using namespace std;


class Location
{
public:
    Location(int xx = 0, int yy = 0)
    {
        X = xx;  Y = yy;  cout << "Constructor Object.\n";
    }

    //copy构造函数  完成对象的初始化
    Location(const Location & obj) //copy构造函数 
    {
        X = obj.X; Y = obj.Y; cout << "Copy Constructor Object.\n";
    }
    Location(const Location & obj1, const Location & obj2) //copy构造函数 
    {
        X = obj1.X + obj2.X; Y = obj1.Y + obj2.Y; cout << "Copy Constructor Object.\n";
    }
    ~Location()
    {
        cout << X << "," << Y << " Object destroyed." << endl;
    }
    int  GetX() { return X; }       int GetY() { return Y; }
private:   int  X, Y;
};

void main()
{

    Location test1(1, 2);
    Location test2(3, 4);

    Location test3(test1, test2);
    cout << test3.GetX() << "\t" << test3.GetY() << endl;

    cout << "hello..." << endl;
    system("pause");
    return;
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

2.4默认构造函数

二个特殊的构造函数

  • 默认无参构造函数 
    当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空

  • 默认拷贝构造函数 
    当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制

一旦类中定义了任何一个有参构造函数或者拷贝构造函数,却没有显示的定义无参构造函数,编译器都不会提供默认的无参构造函数.所以在使用className obj这样声明一个对象的时候,如果不调用有参构造函数或者拷贝构造函数会编译失败!

2.5调用规则

1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数 
2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数 
3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数 
4 )默认拷贝构造函数成员变量简单赋值 
总结:只要你写了构造函数,那么你必须用。


构造析构阶段性总结: 
1)构造函数是C++中用于初始化对象状态的特殊函数 
2)构造函数在对象创建时自动被调用 
3)构造函数和普通成员函数都遵循重载规则 
4)拷贝构造函数是对象正确初始化的重要保证 
5)必要的时候,必须手工编写拷贝构造函数

3.深拷贝和浅拷贝

  • 默认复制构造函数可以完成对象的数据成员值简单的复制
  • 对象的数据资源是由指针指示的堆时,默认复制构造函数仅作指针值复制

 


  • 解决办法是手工实现拷贝构造函数,一旦显示实现拷贝构造函数,编译器不会提供默认的浅拷贝构造函数。

示例代码:



#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;


class Test{
public:
    Test(const char * ptr)
    {
       m_len = strlen(ptr);
       m_p = (char*)malloc(m_len+1);
       strcpy(m_p,ptr);

    }

    Test(const Test & obj)
    {
        m_len = obj.m_len;
        m_p = (char*)malloc(m_len+1);
        strcpy(m_p,obj.m_p);
    }

    ~Test()
    {
        if(NULL != m_p)
        {
            free(m_p);
            m_len = 0;
            m_p = NULL;
        }
    }

    /*重载等号操作符解决编译器默认的等号操作符是浅拷贝的问题*/
    void operator=(Test &obj)
    {
        if (m_p != NULL)
        {
            free(m_p);
            m_p = NULL;
            m_len = 0;
        }
        cout<<"测试有没有调用我。。。。"<<endl;

        //用obj来=自己
        m_len = obj.m_len;
        m_p = (char*)malloc(m_len+1);
        strcpy(m_p,obj.m_p);
    }
private:
    int m_len;
    char *m_p;
protected:

};

//如果没有显示实现Test(const Test & obj)函数,对象析构的时候 出现coredump
void objplaymain()
{
    Test obj1("abcdefg");
    Test obj2 = obj1;  //C++编译器提供的 默认的copy构造函数  浅拷贝
    Test obj3("obj3");

    obj3 = obj1;  // C++编译器提供的 等号操作 也属 浅拷贝
}

int main()
{
    objplaymain();
    cout << "Hello World!" << endl;
    return 0;
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

4.多个对象构造和析构

  • C++中提供了初始化列表对成员变量进行初始化 
    语法规则 
    Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3) 

    // some other assignment operation 
    }

  • 使用初始化列表出现原因: 
    1.必须这样做: 
    如果我们有一个类成员,它本身是一个或者是一个结构,而且这个成员它只有一个带参数的构造函数,而没有默认构造函数,这时要对这个类成员进行初始化,就必须调用这个类成员的带参数的构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。 
    2.类成员中若有const修饰,必须在对象初始化的时候,给const int m 赋值。当类成员中含有一个const对象时,或者是一个引用时,他们也必须要通过成员初始化列表进行初始化,因为这两种对象要在声明后马上初始化,而在构造函数中,做的是对他们的赋值,这样是不被允许的。

  • 注意概念

    • 初始化:被初始化的对象正在创建
    • 赋值:被赋值的对象已经存在

注意: 
1) 成员变量的初始化顺序与声明的顺序相关,与在初始化列表中的顺序无关 
2) 初始化列表先于构造函数的函数体执行



#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;



#include <iostream>
using namespace std;

class A
{
public:
    A(int _a)
    {
        a = _a;
        cout << "构造函数" << "a" << a << endl;
    }

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

protected:
private:
    int a;
};


//1 构造函数的初始化列表  解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数)
//根据构造函数的调用规则 设计A的构造函数, 必须要用;没有机会初始化A
//新的语法  Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
class B
{
public:
    B(int _b1, int _b2) : a1(1), a2(2), c(0)
    {

    }

    B(int _b1, int _b2, int m, int n) : a1(m), a2(n), c(0)
    {
        b1 = _b1;
        b2 = _b2;
        cout <<"B的构造函数"<<endl;
    }
    ~B()
    {
        cout<<"B的析构函数" <<endl;
    }

protected:
private:
    int b1;
    int b2;
    A a2;
    A a1;
    const int c;
};


//2 先执行 被组合对象的构造函数
//如果组合对象有多个,按照定义顺序, 而不是按照初始化列表的顺序

//析构函数 : 和构造函数的调用顺序相反

//3 被组合对象的构造顺序 与定义顺序有关系 ,与初始化列表的顺序没有关系.
//4 初始化列表 用来 给const 属性赋值
void obj10play()
{

    //A a1(10);
    //B ojbB(1, 2);

    //1参数传递
    B ojbB2(1, 2,3, 4);

    //2 调用顺序

    return ;
}

int main()
{
    obj10play();
    system("pause");
    return 0;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

5.调用顺序

1)当类中有成员变量是其它类的对象时,首先调用成员变量的构造函数,调用顺序与声明顺序相同;之后调用自身类的构造函数


2)析构函数的调用顺序与对应的构造函数调用顺序相反

//对象做函数参数
//1 研究拷贝构造
//2 研究构造函数,析构函数的调用顺序

//总结 构造和析构的调用顺序

#include "iostream"
using namespace std;

class ABCD
{
public:
    ABCD(int a, int b, int c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
        printf("ABCD() construct, a:%d,b:%d,c:%d  \n", this->a, this->b, this->c);
    }
    ~ABCD()
    {
        printf("~ABCD() construct,a:%d,b:%d,c:%d  \n", this->a, this->b, this->c);
    }
    int getA()
    {
        return this->a;
    }
protected:
private:
    int a;
    int b;
    int c;
};


class MyE
{
public:
    MyE():abcd1(1,2,3),abcd2(4,5,6),m(100)
    {
        cout<<"MyD()"<<endl;
    }
    ~MyE()
    {
        cout<<"~MyD()"<<endl;
    }
    MyE(const MyE & obj):abcd1(7,8,9),abcd2(10,11,12),m(100)
    {
        printf("MyD(const MyD & obj)\n");
    }

protected:
    //private:
public:
    ABCD abcd1; //c++编译器不知道如何构造abc1
    ABCD abcd2;
    const int m;

};

int doThing(MyE mye1)
{
    printf("doThing() mye1.abc1.a:%d \n", mye1.abcd1.getA());
    return 0;
}

int run2()
{
    MyE myE;
    doThing(myE);
    return 0;
}

//
int run3()
{
    printf("run3 start..\n");

    ABCD(400, 500, 600); //临时对象的生命周期

    ABCD abcd = ABCD(100, 200, 300);
    //若直接调用构造函数呢?
    //想调用构造函数对abc对象进行再复制,可以吗?
    //在构造函数里面调用另外一个构造函数,会有什么结果?

    printf("run3 end\n");
    return 0;
}

int main()
{
    run2();
    run3();
    system("pause");
    return 0;
}


 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98

  • 构造中调用构造

容易引发危险,因为只调用构造函数会产生匿名对象。匿名对象不合适处理会被立即析构,所以相当于深一个层次的构造函数调用毫无意义。 
示例代码如下:

#include "iostream"
using namespace std;


//构造中调用构造是危险的行为
class MyTest
{
public:
    MyTest(int a, int b, int c)
    {
        this->a = a;
        this->b = b;
        this->c = c;
    }

    MyTest(int a, int b)
    {
        this->a = a;
        this->b = b;

        MyTest(a, b, 100); //产生新的匿名对象
    }
    ~MyTest()
    {
        printf("MyTest~:%d, %d, %d\n", a, b, c);
    }

protected:
private:
    int a;
    int b;
    int c;

public:
    int getC() const { return c; }
    void setC(int val) { c = val; }
};

int main()
{
    MyTest t1(1, 2);
    printf("c:%d", t1.getC()); //请问c的值是?
    system("pause");
    return 0;
}

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

6.对象的动态管理

1.new和delete基本语法

1)在软件开发过程中,常常需要动态地分配和撤销内存空间,例如对动态链表中结点的插入与删除。在C语言中是利用库函数malloc和free来分配和撤销内存空间的。C++提供了较简便而功能较强的运算符new和delete来取代malloc和free函数。

注意: new和delete是运算符,不是函数,因此执行效率高。

2)虽然为了与C语言兼容,C++仍保留malloc和free函数,但建议用户不用malloc和free函数,而用new和delete运算符。new运算符的例子:

new int;  //开辟一个存放整数的存储空间,返回一个指向该存储空间的地址(即指针)
new int(100);  //开辟一个存放整数的空间,并指定该整数的初值为100,返回一个指向该存储空间的地址
new char[10];  //开辟一个存放字符数组(包括10个元素)的空间,返回首元素的地址
new int[5][4];  //开辟一个存放二维整型数组(大小为5*4)的空间,返回首元素的地址
float *p=new float (3.14159);  //开辟一个存放单精度数的空间,并指定该实数的初值为//3.14159,将返回的该空间的地址赋给指针变量p
 
 
  • 1
  • 2
  • 3
  • 4
  • 5

3)new和delete运算符使用的一般格式为: 

用new分配数组空间时不能指定初值。如果由于内存不足等原因而无法正常分配空间,则new会返回一个空指针NULL,用户可以根据该指针的值判断分配空间是否成功。

2.类对象的动态建立和释放

使用类名定义的对象都是静态的,在程序运行过程中,对象所占的空间是不能随时释放的。但有时人们希望在需要用到对象时才建立对象,在不需要用该对象时就撤销它,释放它所占的内存空间以供别的数据使用。这样可提高内存空间的利用率。 
C++中,可以用new运算符动态建立对象,用delete运算符撤销对象比如:

Box *pt;  //定义一个指向Box类对象的指针变量pt
pt=new Box;  //在pt中存放了新建对象的起始地址在程序中就可以通过pt访问这个新建的对象。
 
 
  • 1
  • 2

cout<<pt->height;  //输出该对象的height成员
cout<<pt->volume( );//调用该对象的volume函数,计算并输出体积
 
 
  • 1
  • 2

C++还允许在执行new时,对新建立的对象进行初始化。如 
Box *pt=new Box(12,15,18); 
这种写法是把上面两个语句(定义指针变量和用new建立新对象)合并为一个语句,并指定初值。这样更精炼。 
新对象中的height,width和length分别获得初值12,15,18。调用对象既可以通过对象名,也可以通过指针。 
在执行new运算时,如果内存量不足,无法开辟所需的内存空间,目前大多数C++编译系统都使new返回一个0指针值。只要检测返回值是否为0,就可判断分配内存是否成功。 
ANSI C++标准提出,在执行new出现故障时,就“抛出”一个“异常”,用户可根据异常进行有关处理。但C++标准仍然允许在出现new故障时返回0指针值。当前,不同的编译系统对new故障的处理方法是不同的。 
在不再需要使用由new建立的对象时,可以用delete运算符予以释放。如 
delete pt; //释放pt指向的内存空间 
这就撤销了pt指向的对象。此后程序不能再使用该对象。 
如果用一个指针变量pt先后指向不同的动态对象,应注意指针变量的当前指向,以免删错了对象。在执行delete运算符时,在释放内存空间之前,自动调用析构函数,完成有关善后清理工作。


#include <iostream>
using namespace std;

// 1
//      malloc  free                c语言的函数
//      new     delete 操作符  c++的语法

//2     new 基础类型变量  分配数组变量  分配类对象

//3     

分配基础类型
void main01()
{
    // 
    int *p = (int *)malloc(sizeof(int));
    *p = 10;
    //free(p);
    delete p;

    int *p2 = new int; //分配基础类型
    *p2 = 20;
    free(p2);
    //
    int *p3 = new int(30);
    printf("*p3:%d \n", *p3);
    //delete p3;
    free(p3);

    cout<<"hello..."<<endl;
    system("pause");
    return ;
}

//分配数组变量
void main02()
{
    //c语言分配数组
    int *p = (int *)malloc(sizeof(int) * 10);  //int array[10];
    p[0] = 1;
    //free(p);
    delete[] p;

    //c++分配数组 
    int *pArray = new int[10] ;
    pArray[1] = 2;
    //delete [] pArray; //数组不要把[] 忘记
    free(pArray);

    char *pArray2 = new char[25] ; //char buf[25]
    delete [] pArray2;


    cout<<"hello..."<<endl;
    system("pause");
    return ;
}

class Test
{
public:
    Test(int _a)
    {
        a = _a;
        cout<<"构造函数执行" <<endl;
    }

    ~Test()
    {
        cout<<"析构函数执行" <<endl;
    }

protected:
private:
    int a;
};

//分配对象new delete
//相同 和 不同的地方 new能执行类型构造函数   delete操作符 能执行类的析构函数

//  malloc  free函数 C
//1 new     delete 操作符号 c++的关键字
//结论
void main()
{
    //c 
    Test *pT1 = (Test *)malloc(sizeof(Test));
    //free(pT1);
    delete pT1;

    //c++
    Test *pT2 = new Test(10);
    //delete pT2;
    free(pT2);

    cout<<"hello..."<<endl;
    system("pause");
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值