49. 构造与析构(上)

对象的初始化

生活中存在的对象都是被初始化后才上市的,初始状态是对象普遍存在的一个状态的

一般而言所有的对象都需要一个确定的初始状态

  • 解决方案

    • 为每个类都提供一个public的initialize函数

    • 对象创建后立即调用initialize函数进行初始化

initialize只是一个普通的函数,必须显示的调用,一旦由于失误的原因,对象没有初始化,那么结果将是不确定的(随机值),没有初始化的对象,其内部成员变量的值是不定的。(上述初始化方式不推荐,请看C++中的构造函数)

C++中的构造函数

C++中的类可以定义与类名相同的特殊成员函数

这种与类名相同的成员函数叫做构造函数

构造函数在定义时可以有参数,但是没有任何返回类型的声明

构造函数的调用

  • 一般情况下C++编译器会自动调用构造函数

  • 在一些情况下则需要手工调用构造函数

Test t1(4);//自动调用构造函数 .推荐推荐
Test t2 = 5;//自动调用构造函数 
Test t3 = Test(6);//主动调用构造函数
Test tA[3] = {Test(1), Test(2), Test(3)};//主动调用构造函数

例:

#include <stdio.h>

class Test
{
private:
    int i;
    int j;
    int k;
    
public:
    Test(int v)
    {
        i = v;
        j = v;
        k = v;
    }
    
    void print()
    {
        printf("i = %d, j = %d, k = %d\n", i, j, k);
    }
};

int main()
{
    Test t1(4);//自动调用构造函数★推荐这个调用有参构造函数
    Test t2 = 5;//自动调用构造函数
    Test t3 = Test(6);//主动调用构造函数
    
    t1.print();
    t2.print();
    t3.print();
  
    //主动调用构造函数//Test(int v)
    Test tA[3] = {Test(1), Test(2), Test(3)};
    for(int i=0; i<3; i++)
    {
        tA[i].print();
    }
    
    printf("Press any key to continue...");
    getchar();
    return 0;
}
result:
i = 4, j = 4, k = 4
i = 5, j = 5, k = 5
i = 6, j = 6, k = 6
i = 1, j = 1, k = 1
i = 2, j = 2, k = 2
i = 3, j = 3, k = 3
Press any key to continue...

成员函数的重载

  • 类的成员函数和普通函数一样可以进行重载,并遵守相同的重载规则

#include <stdio.h>

class Test
{
private:
    int i;
    int j;
    int k;
    
public:
    Test()
    {
        i = 0;
        j = 0;
        k = 0;
    }
    
    Test(int v)
    {
        i = v;
        j = v;
        k = v;
    }
/*
	Test(),Test(int v)构造函数重载
	当谈论构造函数的重载时,是指通过定义多个具有不同参数列表的构造函数来支持多种对象初始化方式的能力。
*/    


    void print()
    {
        printf("i = %d, j = %d, k = %d\n", i, j, k);
    }
    
    void print(int v)
    {
        printf("v = %d\n", v);
    }
};

int main()
{
    Test t1(4);//4,4,4
    Test t2 = 5;//5,5,5
    Test t3 = Test(6);//6,6,6
    Test t4;//0,0,0
    
    t4.print();
    t1.print();
    t2.print();
    t3.print();
    
    Test tA[3];//Test()
    
    for(int i=0; i<3; i++)
    {
        tA[i].print();
    }
    
    printf("Press any key to continue...");
    getchar();
    return 0;
}
result:
i = 0, j = 0, k = 0
i = 4, j = 4, k = 4
i = 5, j = 5, k = 5
i = 6, j = 6, k = 6
i = 0, j = 0, k = 0
i = 0, j = 0, k = 0
i = 0, j = 0, k = 0
Press any key to continue...

两个特殊的构造函数

  • 无参构造函数

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

  • 拷贝构造函数

    • 当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制。    Test(const Test& obj) 是拷贝构造函数,因为它接受一个对同类对象常量引用作为参数。

注意:

  • 当类中没有定义任何一个构造函数,C++编译器会为提供无参构造函数和拷贝构造函数

  • 当类中定义了任意的非拷贝构造函数,C++编译器不会为提供无参构造函数

      • 如果类中没有显式定义拷贝构造函数,编译器会尝试自动生成一个。

      • 当需要自定义复制行为时,最好显式定义拷贝构造函数。

#include <stdio.h>

/*
    注意:
    1. 当类中没有定义任何一个构造函数,C++编译器会为提供无参构造函数和拷贝构造函数
    2. 当类中定义了任意的非拷贝构造函数时,C++编译器不会为提供无参构造函数 
*/

class Test
{ 
public:
    Test()
    {
        printf("Test()\n");
    }
    
/*
	Test() 和 Test(const Test& obj) 不是函数重载
	Test(const Test& obj) 是拷贝构造函数,因为它接受一个对同类对象的常量引用作为参数。
	在C++中,拷贝构造函数是一个特殊的构造函数,用于创建一个新对象作为现有对象的副本。
    
*/
    Test(const Test& obj)
    {
        printf("Test(const Test& obj)\n");
    }
};

int main()
{
    Test t1;//Test()
    Test t2 = t1;//Test(const Test& obj)
    
    printf("Press any key to continue...");
    getchar();
    return 0;
}
result:
Test()
Test(const Test& obj)
Press any key to continue...

当类中没有定义任何一个构造函数,编译器提供无参构造函数拷贝构造函数(会主动将成员变量的值进行复制)

#include <stdio.h>


class Test
{ 
private:
    int i = 1;
    int j = 2;
    int k = 3;
    
public:
    void print()
    {
        printf("i = %d, j = %d, k = %d\n", i, j, k);
    }
};

int main()
{
    Test t1;
    Test t2 = t1;//没有,自己造,浅拷贝
    
    t1.print();
    t2.print(); 
    
    printf("Press any key to continue...");
    getchar();
    return 0;
}
result:
i = 1, j = 2, k = 3
i = 1, j = 2, k = 3
/*
	当类中没有定义任何一个构造函数,C++编译器会为提供无参构造函数和拷贝构造函数(会主动将成员变量的值进行复制)
*/
Press any key to continue...

数组类的创建(进阶版在下一章)

Array.h

#ifndef _ARRAY_H_
#define _ARRAY_H_

class Array
{
private:
    int mLength;//数组长度
    int* mSpace;//数组空间

public:
    Array(int length);//构造函数
    Array(const Array& obj);//拷贝构造函数
    int length();//访问private
    void setData(int index, int value);//初始化数组值
    int getData(int index);//获得数组值
    void destory();
};

#endif

Array.cpp


#include "Array.h"

Array::Array(int length)
{
    if( length < 0 )
    {
        length = 0;
    }
    
    mLength = length;
    mSpace = new int[mLength];
}

Array::Array(const Array& obj)//★这里的拷贝构造函数不能使用默认的
{
    mLength = obj.mLength;
    
    mSpace = new int[mLength];//开辟一个新空间去存放数组
    
    for(int i=0; i<mLength; i++)
    {
        mSpace[i] = obj.mSpace[i];
    }
}

int Array::length()//返回数组类长度
{
    return mLength;
}

void Array::setData(int index, int value)
{
    mSpace[index] = value;
}

int Array::getData(int index)
{
    return mSpace[index];
}

void Array::destory()
{
    mLength = -1;
    delete[] mSpace;
}

main.cpp

#include <stdio.h>
#include "Array.h"

int main()
{
    Array a1(10);//数组长度,开辟空间
    
    for(int i=0; i<a1.length(); i++)//赋值
    {
        a1.setData(i, i);
    }
    
    for(int i=0; i<a1.length(); i++)//得到值
    {
        printf("Element %d: %d\n", i, a1.getData(i));
    }
    
    Array a2 = a1;//调用拷贝构造函数,这里用深拷贝
    
    for(int i=0; i<a2.length(); i++)
    {
        printf("Element %d: %d\n", i, a2.getData(i));
    }
    
    a1.destory();
    a2.destory();
    
    printf("Press any key to continue...");
    getchar();
    return 0;
}

g++ -o main main.cpp Array.cpp

./main.exe

Element 0: 0
Element 1: 1
Element 2: 2
Element 3: 3
Element 4: 4
Element 5: 5
Element 6: 6
Element 7: 7
Element 8: 8
Element 9: 9
Element 0: 0
Element 1: 1
Element 2: 2
Element 3: 3
Element 4: 4
Element 5: 5
Element 6: 6
Element 7: 7
Element 8: 8
Element 9: 9
Press any key to continue...

浅拷贝和深拷贝

浅拷贝会导致拷贝的对象和原对象共享同一块内存空间(可能会导致内存被重复释放,使得程序崩溃)

小结

构造函数是C++中用于初始化对象状态的特殊函数

构造函数在对象创建时自动被调用

构造函数和普通成员函数都遵循重载规则

拷贝构造函数是对象正确初始化的重要保证

思考

可以直接调用构造函数吗?直接调用构造函数(避免!!!)会有什么情况发生呢?(应当避免写此类代码:不要在一个构造函数里面调用另一个构造函数

  • 可以直接调用构造函数,直接调用构造函数将得到一个临时对象。

#include <stdio.h>

class Test
{
private:
    int mI;
    int mJ;
    const char* mS;
public:
    Test()
    {
        printf("Test()\n");
        
        mI = 0;
        mJ = 0;
    }
    
    Test(const char* s)
    {
        printf("Test(const char* s)\n");
        
        Test();//在有参构造函数中调用无参构造函数,能否执行那段代码?
        
        mS = s;
    }
    
    ~Test()
    {
        printf("~Test()\n");
    }
    
    void print()
    {
        printf("mI = %d, mJ = %d, mS = %s\n", mI, mJ, mS);
    }
};

void run()
{
    Test t = Test("Delphi Tang"); // Test t("Delphi Tang");
    
    t.print();
}

int main()
{
    run();
    
    printf("Press any key to continue...");
    getchar();
    return 0;
}
result:
Test(const char* s)
Test()   //生成临时对象。调用构造函数
~Test()  //临时对象马上结束,调用析构函数
mI = 15138816, mJ = -1, mS = Delphi Tang
~Test()
Press any key to continue...
  • 构造函数只能 被编译器自动调用 或者 声明一个类的对象时手动调用

  • 上述程序中的情形不是这两者。这样调用会导致产生临时对象。

  • Test()一结束,临时对象马上被销毁,调用析构

  • mI和mJ没有被赋值,是随机的

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值