八、构造与析构函数

一、对象初始化
如何给对象进行初始化:
为每个类都提供一个public的initialize函数,对象创建后立即调用initialize函数进行初始化

#include<iostream>
using namespace std;
class student
{
private:
    int i;
public:
    void initialize()
    {
        i = 5;
    }
    int get()
    {
        return i;
    }
};
int main()
{
    student t1;
    t1.initialize();//定义对象后需马上调用初始化函数
    cout<<t1.get()<<endl;
    cin.get();
    return 0;
}

能工作了,但不爽:
initialize只是一个普通的函数,必须显示的调用
一旦失误,对象没有初始化,那么结果是不确定的(没有初始化的对象,其内部成员变量的值是不确定的)
如上面的程序,创建对象后不调用initialize函数会输出随机值
二、c++里的构造函数(与类名相同,可以有参数但无返回值)
没有返回值和返回值是void是完全不同的
1.构造函数的调用:
一般情况下c++编译器会自动调用构造函数,但在一些情况下则需要手工调用构造函数

#include<iostream>
using namespace std;
class student
{
private:
    int i;
public:
    student(int v)//构造函数,可以有参数,但无返回值
    {
        i = v;
    }
    int get()
    {
        return i;
    }
};
int main()
{
    student t1(3);//自动初始化
    student t2=4;//自动初始化
    student t3 = student(5);//手动初始化
    cout<<t1.get()<<endl;//3
    cout<<t2.get()<<endl;//4
    cout<<t3.get()<<endl;//5
    cin.get();
    return 0;
}
三种方法无本质区别

*必须手动初始化的情况:
Test tA[3] = {t1(3),t2(4),t3(5)};这是一个类数组*
三、成员函数的重载:
类的成员函数一样可以重载,并遵守相同的重载规则,构造函数也可以重载(调用重载构造函数时也遵循规则)

#include<iostream>
using namespace std;
class student
{
private:
    int i;
    int j;
    int k;
public:
    student(int v)//构造函数,可以有参数,但无返回值
    {
        i = v;
        j = v;
        k = v;
    }
    student()
    {
        i = 3;
        j = 4;
        k = 5;
    }
    void print()//普通的成员函数
    {
        cout<<i<<endl;
        cout<<j<<endl;
        cout<<k<<endl;
    }
    void print(int v)
    {
        cout<<i<<endl;
        cout<<j<<endl;
        cout<<k<<endl;
        cout<<"hello"<<endl;
    }
};
int main()
{
    student t1;//调用的构造函数是student()
    student t2(8);//调用的构造函数是student(int v)
    t2.print();
    t1.print(5);//调用的是void print(int v),因为有参数传进去
    t1.print();//调用的是void print(),因为没有参数传进去
    cin.get();
    return 0;
}  

student tA[3];//为何此处没有初始化也可以呢,因为类里的student()没有参数,就默认调用这个函数了
四、两个特殊的构造函数(无参构造函数和拷贝构造函数)
1.无参构造函数:
当类中没有定义构造函数时,编译器提供一个无参构造函数,并且其函数体为空
2.拷贝构造函数:
当类中没有定义拷贝函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量成员变成员变量的值的复制

#include<iostream>
using namespace std;
class Test
{ 
private:
    int a;
public:
    Test(int b)//构造函数
    {
        a = b;
    }
    void show()
    {
        cout<<a<<endl;
    }
};
int main()
{
    Test t1(100);
    Test t2 = t1; 
    t2.show();//打印100,调用了默认的拷贝构造函数,将t1里的值拷贝到了t2
    cin.get();
    return 0;
}
#include<iostream>
using namespace std;
class Test
{ 
private:
    int a;
public:
    Test(int b)//构造函数
    {
        a = b;
    }
    Test(const Test& obj) //拷贝构造函数的形态
    {
        cout<<"hello"<<endl;
    }
    void show()
    {
        cout<<a<<endl;
    }
};
int main()
{
    Test t1(100);
    Test t2 = t1; //打印出hello,调用了自定义的拷贝构造函数
    t2.show();//打印乱码,有了自定义的拷贝构造函数,不再调用默认的,而自定义里面的拷贝构造函数没有将a的值拷贝过来,故     cin.get();// 输出乱码
    return 0;
}

五、数组类的创建
Array.h

#ifndef _ARRAY_H_
#define _ARRAY_H_
class Array
{
private:
    int mlenth;
    int *mspace;
public:
    Array(int lenth);
    int Lenth();
    void SetData(int index, int data);
    int GetData(int index);
    void Destroy();
};
#endif

Array.cpp

#include"Array.h"
Array::Array(int lenth)
{
    if(0 > lenth)
    {
        lenth = 0;
    }
    else
    {
        mlenth = lenth;
        mspace = new int[lenth];
    }
}
int Array::Lenth()
{
    return mlenth;
}
void Array::SetData(int index, int data)
{
    mspace[index] = data;
}
int Array::GetData(int index)
{
    return mspace[index];
}
void Array::Destroy()
{
    mlenth = -1;
    delete[] mspace; 
}
#include<iostream>
#include"Array.h"
using namespace std;
int main()
{
    Array a1(10);
    for(int i = 0; i < 10; i++)
    {
        a1.SetData(i, i);
    }
    for( int i = 0; i < 10; i++ )
    {
        cout<<a1.GetData(i);
    }
    Array a2(10);
    for(int i = 0; i < 10; i++)
    {
        a2.SetData(i, i);
    }
    for( int i = 0; i < 10; i++ )
    {
        cout<<a2.GetData(i);
    }
    a1.Destroy();
    a2.Destroy();
    cin.get();
    return 0;
}

*注:以上代码有一个非常严重的bug,a1.Destroy()和a2.Destroy();对同一片内存释放了两次
因为使用默认的拷贝构造函数只是简单的值拷贝
解决方法:自定义一个拷贝构造函数:*

#ifndef _ARRAY_H_
#define _ARRAY_H_
class Array
{
private:
    int mlenth;
    int *mspace;
public:
    Array(int lenth);
    Array(Array const& obj);//自定义一个拷贝函数
    int Lenth();
    void SetData(int index, int data);
    int GetData(int index);
    void Destroy();
};
#endif
#include"Array.h"
Array::Array(int lenth)
{
    if(0 > lenth)
    {
        lenth = 0;
    }
    else
    {
        mlenth = lenth;
        mspace = new int[lenth];
    }
}
Array::Array(Array const& obj)
{
    mlenth = obj.mlenth;
    mspace = new int[mlenth];//自定义的拷贝函数里边又申请了一片内存
    if(mlenth > 0)
    {
        for(int i = 0; i<mlenth; i++)
        {
            mspace[i] = obj.mspace[i];
        }
    }
}
int Array::Lenth()
{
    return mlenth;
}
void Array::SetData(int index, int data)
{
    mspace[index] = data;
}
int Array::GetData(int index)
{
    return mspace[index];
}
void Array::Destroy()
{
    mlenth = -1;
    delete[] mspace; 
}
#include<iostream>
#include"Array.h"
using namespace std;
int main()
{
    Array a1(10);
    for(int i = 0; i < 10; i++)
    {
        a1.SetData(i, i);
    }
    for( int i = 0; i < 10; i++ )
    {
        cout<<a1.GetData(i);
    }
    Array a2(10);
    for(int i = 0; i < 10; i++)
    {
        a2.SetData(i, i);
    }
    for( int i = 0; i < 10; i++ )
    {
        cout<<a2.GetData(i);
    }
    a1.Destroy();
    a2.Destroy();
    cin.get();
    return 0;
}

六、c++中的类可以使用其它类来定义成员变量

#include<iostream>
using namespace std;
class M
{
private:
    int a;
public:
    M()
    {
        a = 5;
    }
    int get()
    {
        return a;
    }
};
class N
{
private:
    M a1;//类N的成员变量是其他类的对象
public:
    void print()
    {
        cout<<a1.get()<<endl;//成功打印出5
    }
};
int main()
{
    N a2;
    a2.print();
    cin.get();
    return 0;
}

七、初始化列表(对成员变量进行初始化)
注:成员变量的初始化顺序与声明有关,与在初始列表中的顺序无关
初始化列表先于构造函数的函数体执行

#include<iostream>
using namespace std;
class M
{
private:
    int a;
public:
    M(int i)
    {
        a = i;
        cout<<"hello1"<<endl;
    }
    int get()
    {
        return a;
    }
};
class N
{
private:
    M a1;//先声明a1
    const int c;
public:
    N():c(5),a1(10)//注意参数列表的格式,且参数列表的执行顺序与声明时的顺序有关,参数列表先于构造函数执行
    {
        cout<<"hello2"<<endl;
    }
};
int main()
{
    N a2;
    cin.get();
    return 0;
}

注:
类中的const成员是肯定会被分配空间的
类中的const成员变量只是一个只读变量
编译器无法直接得到const成员变量的初始值,因此无法进入符号表成为真正意义上的常量
初始化与赋值不同:
初始化是用已存在的对象或值对 正在创建的对象 进行初值设置
赋值是用已存在的对象或值对 已经存在的对象 进行值设置

七、对象的销毁
那么如何进行对象的销毁?
解决方法:
(1).为每个类都提供一个public的destroy函数
对象不再需要时需要立即调用destroy函数进行清理(如前面的数组类的创建的例子)
但是类似的问题:
destroy只是一个普通的函数,必须显示的调用
如果对象销毁前没有做清理工作,那么很可能造成资源的泄露

(2). c++的类定义可以定义一个特殊的成员函数清理对象
这个特殊的成员函数叫做析构函数
定义:~ClassName()
析构函数没有任何的参数也没有任何的返回值类型
析构函数在对象被销毁时自动被调用

#include <stdio.h>
class Test
{
private:
    int mI;
public:
    Test(int i) : mI(i)
    {
        printf("Test(), mI = %d\n", mI);
    }

    ~Test()//析构函数
    {
        printf("~Test(), mI = %d\n", mI);
    }
};
void run()
{
    Test t1(1);
    Test t2(2);
}
int main()
{
    run();

    printf("Press any key to continue...");
    getchar();

    return 0;
}

八:数组类的进化
将Destroy()函数改成析构函数即可,程序执行完会自动调用
九:构造与析构函数的调用秩序
当类中有成员变量是其他类的对象时
首先调用成员变量的构造函数,且调用顺序与声明顺序相同
之后调用自身类的构造函数
析构函数的调用秩序与对应的构造函数调用秩序相反

#include<iostream>
using namespace std;
class M
{
private:
    int a;
public:
    M(int i)
    {
        a = i;
        cout<<"hello1"<<endl;
    }
    int get()
    {
        return a;
    }
};
class G
{
public:
    G(int i)
    {
        cout<<"hello3"<<endl;
    }
};
class N
{
private:
    M a1;
    G a2;
    const int c;
public:
    N():c(5),a2(10),a1(1)
    {
        //cout<<c<<endl;//说明参数列表先于构造函数执行
        //c = 6;//发生错误,说明c是只读变量
        cout<<"hello2"<<endl;
    }
};
int main()
{
    N a2;
    cin.get();
    return 0;
}

先打印hello1,再打印hello2,最后是hello3

十、是否可以直接调用构造函数?
可以直接调用,会得到一个临时对象,但是这个临时对象马上就被析构了
构造函数只能被编译器自动调用或者声明一个对象时手工调用
如落不然会生成了一个临时对象,那行结束后马上被析构

#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;
}

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值