0
点赞
收藏
分享

微信扫一扫

黑马程序员c++教程,利用类模板实现数组封装案例中,一种断点bug的产生原因及解决方案

小迁不秃头 2022-04-17 阅读 23

项目场景:

黑马程序员c++教程中利用类模板实现数组封装案例

在黑马程序员c++教程中,老师在讲解类模板时,使用类模板创建了数组类。通过这一类模板,可以创建不同数据类型的数组,并对数组进行一系列操作。其中,我们可以对运算符“[]”进行重载,来实现通过“方括号内下标”的方式直接访问数组元素。


问题描述

在测试代码的过程中,如果通过下标的方式访问数组元素,会出现触发断点的bug

以下是数组类模板的实现代码:

#pragma once
#include <iostream>
#include <string>
using namespace std;

template<typename T>
class MyArray
{
public:
	//构造函数(容量)
	MyArray(int capacity);
	//拷贝构造
	MyArray(const MyArray& arr);
	//operator=
	MyArray& operator=(const MyArray& arr);
	//尾插元素
	void PushBack(T x);
	//尾删元素
	void PopBack(T x);
	//利用[下标]访问数组元素
	T& operator[](const int i);
	//获取数组大小
	int GetSize();
	//获取数组容量
	int GetCapacity();
	//析构函数
	~MyArray();
private:
	T* m_arr;
	int m_size;
	int m_capacity;
};

//构造函数(容量)
template<typename T>
MyArray<T>::MyArray(int capacity)
{
	this->m_arr = new T(capacity);
	this->m_size = 0;
	this->m_capacity = capacity;
	cout << "MyArray的构造函数调用" << endl;
}

//拷贝构造函数
//需要深拷贝
template<typename T>
MyArray<T>::MyArray(const MyArray<T>& arr)//
{
	this->m_capacity = arr.m_capacity;
	this->m_size = arr.m_size;
	this->m_arr = new T(m_capacity);
	for (int i = 0; i < this->m_size; i++)
	{
		this->m_arr[i] = arr.m_arr[i];
	}
	cout << "MyArray的拷贝构造函数调用" << endl;
}

//operator=
//同样需要深拷贝
template<typename T>
MyArray<T>& MyArray<T>::operator=(const MyArray<T>& arr)//
{
	//先检验原数组是否已经存在数据,若已存在,先删除再拷贝
	if (this->m_arr != NULL)
	{
		delete[]this->m_arr;
		this->m_size = 0;
		this->m_capacity = 0;
		this->m_arr = NULL;
	}
	this->m_capacity = arr.m_capacity;
	this->m_size = arr.m_size;
	this->m_arr = new T(m_capacity);
	for (int i = 0; i < this->m_size; i++)
	{
		this->m_arr[i] = arr.m_arr[i];
	}
	cout << "MyArray的operator=函数调用" << endl;
	return *this;
}

//尾插元素
template<typename T>
void MyArray<T>::PushBack(T x)
{
	if (this->m_size == this->m_capacity)
	{
		cout << "当前数组已满,无法插入!" << endl;
	}
	else
	{
		this->m_arr[this->m_size] = x;
		(this->m_size)++;
	}
}

//尾删元素
template<typename T>
void MyArray<T>::PopBack(T x)
{
	if (this->m_size == 0)
	{
		cout << "当前数组为空,无法删除!" << endl;
	}
	else
	{
		(this->m_size)--;
	}
}

//利用[下标]访问数组元素
template<typename T>
T& MyArray<T>::operator[](const int i)
{
	return this->m_arr[i];
}

//获取数组大小
template<typename T>
int MyArray<T>::GetSize()
{
	return this->m_size;
}
//获取数组容量
template<typename T>
int MyArray<T>::GetCapacity()
{
	return this->m_capacity;
}

//析构函数
template<typename T>
MyArray<T>::~MyArray()
{
	delete[]this->m_arr;
	this->m_size = 0;
	this->m_capacity = 0;
	this->m_arr = NULL;
	cout << "MyArray的析构函数调用" << endl;
}

以下是测试代码:

int main()
{
	MyArray<int> arr(5);
	int capacity = arr.GetCapacity();
	for (int i = 0; i < capacity; i++)
	{
		arr.PushBack(i);
	}

	int size = arr.GetSize();
	for (int i = 0; i < size; i++)
	{
		cout << arr[i]<<endl;
	}
}

以下是出现的断点bug
在通过“[下标]”的方式访问利用类模板创建的数组时,触发断点在通过“[下标]”的方式访问利用类模板创建的数组时,触发断点
最初笔者以为,是因为重载[]运算符的代码有误,导致无法通过方括号内下标的方式访问数组,但将这段代码注释掉,即不使用重载的运算符后,出现了更严重的bug:
在这里插入图片描述


原因分析:

在笔者将自己的代码与视频教程中的代码对比后,笔者发现了原因。
在有参构造函数、拷贝构造函数,以及重载赋值运算符函数这三个函数中,需要在堆区上开辟一段连续的内存,用来存储数组的元素。
要做到这一点,就需要使用new关键字
具体语法为:

接收指针p = new 数据类型 [元素个数]

其中,用来包含元素个数的是中括号“[]”
但笔者在代码中却使用了小括号“()”
以有参构造函数为例:
错误的写法:

template<typename T>
MyArray<T>::MyArray(int capacity)
{
	this->m_arr = new T(capacity);//错误的写法
	this->m_size = 0;
	this->m_capacity = capacity;
}

正确的写法:

template<typename T>
MyArray<T>::MyArray(int capacity)
{
	this->m_arr = new T[capacity];//正确的写法
	this->m_size = 0;
	this->m_capacity = capacity;
}

new T[capacity]的含义是,在堆区开辟大小为capacity个T类型数据的内存,并返回第一个数据的指针。
new T(capacity)的含义则是,在堆区开辟大小为一个T类型数据的内存,将这个T类型数据的值初始化为capacity。

正是因为笔者写成了后者,所以才导致在测试时,以为创建了5个元素大小的数组,实际上只创建了一个值为5的元素。
而此时size又是5,自然会导致指针访问越界,从而触发断点。

int main()
{
	MyArray<int> arr(5);
	int capacity = arr.GetCapacity();
	for (int i = 0; i < capacity; i++)
	{
		arr.PushBack(i);
	}
	int size = arr.GetSize();
	for (int i = 0; i < size; i++)
	{
		cout << arr[i]<<endl;
	}
}

解决方案:

虽然解决了这一bug,但笔者尚未明白的是,在原有的错误代码下,用尾插函数向数组中插入元素时,指针应该也是越界访问的,但却没有因此崩溃,在调试时甚至将该段连续的内存成功地依次赋值(包括越界访问的部分)。反倒是后面执行的打印操作导致程序崩溃。简单点说,两部分都存在越界访问,结果前面没崩,后面反而崩了。难道原因在于,前面使用的是普通成员函数,后面使用的是重载的运算符?

举报

相关推荐

0 条评论