C++类class:构造函数,析构函数,拷贝构造函数

C++中定义一个类的时候,编译器会提供几个构造函数和析构函数,以及拷贝构造函数,但是我们在使用的时候依靠编译器给的函数往往是无法构成我们想要的完整功能,此时则需要我们自己定义这几个成员函数。

默认构造函数根据默认值初始化类中成员,编译器自带一个空执行默认构造函数。

有参构造函数根据传入参数初始化类中成员。

拷贝构造函数则是类对象需要拷贝时用于拷贝的构造函数,编译器自带一个直接复制所有成员的拷贝构造函数。

析构函数是在对象生命周期结束时用于释放内存的函数,编译器自带一个空执行析构函数。

值得注意的是,在类中如果申请了堆内存,一定要重写拷贝构造函数和析构函数,不然在运行过程中会产生野指针重复析构等bug。

以一个数组类c_array为例:

class c_array
{
private:
    int arr_size;
    int* arr;
};

c_array类的成员分别是arr_size以及arr,分别为数组的元素个数以及数组的存放地址。

首先我们需要一个默认构造函数,默认构造函数是在没有参数的情况下实例化一个类对象时使用的构造函数,用于初始化类对象。根据我们对c_array的需求,可以用于接收arr_size并根据其创建一个空数组。

c_array::c_array()
{
    getsize();
    arr = new int[arr_size];   
}

根据两个初始化目标,getsize()函数用于接收输入值,arr则是根据其具体数值在栈区内存开辟了一个用于存放数组的空间以便接收具体的数值。getsize()作为类内的一个成员函数实现,以便其他函数调用:

void c_array::getsize()
{
    cout<< "维数:" <<  endl;
    cin >> arr_size;
}

c_array a;//创建一个实例

此时实例化一个c_array类时,会提示输入维数,输入维数之后构造函数则会创建一个对应的空数组。

但是每次创建一个新变量都需要手动输入数组的维数显然不方便,此时则需要有参构造函数,在实例化类对象时传入参数,直接根据参数获得维数并创建新的空数组:

c_array::c_array(int size) :arr_size(size), arr(new int[size])
{
}

然后需要注意的是析构函数,由于我们在创建动态数组时开辟了堆内存空间,堆内存的特点就是要手动释放,因此在析构时应该释放堆内存:

c_array::~c_array()
{
    //释放堆区内存
    if (arr != NULL)
    {
        delete[] arr;
        arr = NULL;
    }
}

如果不利用析构函数释放申请的堆内存,程序完整运行结束之前内存则会一直被占用,不利于计算资源的合理使用。

接着就是重点,拷贝构造函数的重写。由于编译器默认的拷贝构造函数为直接复制成员函数,称为浅拷贝,相当于:

c_array::c_array(const c_array& a)
{
    this->arr_size = a.arr_size;
    this->arr = a.arr;
}

此时会出现一个问题,arr是存放我们数组的指针,此时拷贝构造函数直接将a中数组的地址复制给了this中数组的地址,导致被复制出来的对象和原对象指向的是同一个堆内存

此时修改其中一个实例数组中的变量,另一个也会随之更改,当其中一个对象1结束其生命周期被析构函数释放时,另一个对象2指向的内存就变成了空,成为一个野指针。此时再使用对象2时会出现乱码。

当对象2结束其生命周期被析构函数释放时,由于其成员中的arr指针成为了野指针,释放其指向的内存空间则会产生越界行为而使程序崩溃。

因此,如果想要正常实现其功能,应该在拷贝对象时重新申请堆内存,并将其中的变量逐一复制,称为深拷贝:

c_array::c_array(const c_array& a)
{
    //深拷贝,重新申请堆区内存,默认拷贝只拷贝堆地址,会重复析构空内存
    arr_size = a.arr_size;
    arr = new int[arr_size];
    for (int i = 0; i < arr_size; i++)
    {
        arr[i] = a.arr[i];
    }
}

手动重写深拷贝构造函数后就可以正常使用其功能了,在后续重载类运算符时会多次产生类似的情况,从内存的角度理解变量有益于加深对程序的理解。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
构造函数析构函数C++中的两个重要函数。 构造函数用于初始化对象的数据成员,在对象创建时自动调用,可以有参数也可以没有参数。 析构函数用于清理对象占用的资源,在对象销毁时自动调用,没有参数也没有返回值。 下面是一个简单的示例,包含了构造函数析构函数的实现: ```c++ #include <iostream> using namespace std; class MyClass { public: MyClass(); // 声明默认构造函数 MyClass(int num); // 声明带参数构造函数 ~MyClass(); // 声明析构函数 private: int *ptr; }; // 默认构造函数 MyClass::MyClass() { cout << "Default constructor called." << endl; ptr = new int; *ptr = 0; } // 带参数构造函数 MyClass::MyClass(int num) { cout << "Parameterized constructor called." << endl; ptr = new int; *ptr = num; } // 析构函数 MyClass::~MyClass() { cout << "Destructor called." << endl; delete ptr; } int main() { MyClass obj1; // 调用默认构造函数 MyClass obj2(10); // 调用带参数构造函数 return 0; } ``` 输出结果: ``` Default constructor called. Parameterized constructor called. Destructor called. Destructor called. ``` 在上面的例子中,我们定义了一个名为MyClass,并实现了默认构造函数、带参数构造函数析构函数。 在main函数中,我们创建了两个MyClass对象,一个使用默认构造函数创建,一个使用带参数构造函数创建。在程序结束时,析构函数会自动被调用,释放对象占用的资源。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值