26、问题分析

本文探讨了异常处理的最佳实践,包括空指针异常的安全处理,链表删除操作的优化,遍历与删除混合操作的问题解决,静态链表中数据元素删除的效率改进,以及析构函数的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、创建异常对象时的空指针问题

NullPointerException npe;

Exception(0);

init(0,NULL,0);

m_message=strdup(0);

m_message=strdup(message? strdup(message):NULL);  

//为了代码安全,message有空指针的可能。26课

strdup通过strlen获取长度,没有判断是不是空指针,默认不是空指针。

改:m_message=(message? strdup(message):NULL);

2、LinkList中的数据元素删除

#include <iostream>
#include "LinkList.h"
using namespace std;
using namespace DTLib;
class Test : public Object
{
    int m_id;
public:
    Test(int id = 0)
    {
        m_id = id;
    }
    ~Test()
    {
        if( m_id == 1 )
        {
            throw m_id;
        }
    }
};
int main()
{
    LinkList<Test> list;
    Test t0(0), t1(1), t2(2);
    try
    {
        list.insert(t0);
        list.insert(t1);  //t1在析构函数抛出异常
        list.insert(t2);
        list.remove(1); //删除1时析构抛出异常
    }
    catch(int e)
    {
        cout << e << endl;
        cout << list.length() << endl; //期望打印2
    }
    return 0;

}

程序停止,优化见程序

remove:原程序先destroy,后减减。

改:先m_length--; 后destroy(toDel);

clear: 先destroy(toDel), 后m_length=0;不安全

改:先m_length--,后destroy(toDel);

3、LinkList中遍历操作与删除操作混合使用

LinkList<int>list;
for(int i=0;i<5;i++)
{
list.insert(0,i);
}
for(list.move(0);!list.end();list.next()) //O(n)
{
cout<<list.current()<<endl;

}


#include <iostream>
#include "LinkList.h"
using namespace std;
using namespace DTLib;
int main()
{
    LinkList<int> list;
    for(int i=0; i<5; i++)
    {
        list.insert(i);
    }
    for(list.move(0); !list.end(); list.next())
    {
        if( list.current() == 3 )
        {

            list.remove(list.find(list.current()));

            cout<<list.current()<<endl;

//删除的时候current还指向了一个值,打印出一个随机值

        }
    }
    for(int i=0; i<list.length(); i++)
    {
        cout << list.get(i) << endl;
    }
    return 0;

}删除之后,m_current任然指向一个随机值

改:if(m_current==toDel){

m_current=toDel->next;}

4、StaticLinkList 中数据元素删除时的效率问题

void destroy(Node* pn)
{
SNode* space=reinterpret_cast<SNode*>(m_space);//指针运算必须要指针转换
SNode* psn=dynamic_cast<SNode*>(pn);
for(int i=0;i<N;i++)
{
if(pn == (space+i))  //第i号内存单元需要归还
{
m_used[i]=0;
psn->~SNode(); //空间归还,对象析构,即可跳出循环

}}}

改:if(pn == (space+i)) 
{
m_used[i]=0;

psn->~SNode(); 

break;}

5、StaticLinkList是否需要提供析构函数:一个类是否提供析构函数由是否申请系统资源决定,如果类的构造函数申请了系统资源,就必须提供析构函数,在析构函数中释放对应的系统资源。

 //如果没有任何继承关系,从资源角度不用提供析构函数。由于StaticLinkList类继承自LinkList,由于父类中析构函数调用了clear虚函数, 注意构造函数和析构函数不能发生多态,所以这里没有发生多态,子类没有重写clear函数,但是LinkList中的clear却调用了destroy函数, 而子类和父类都定义了destroy,这意味着父类的析构函数被调用的时候始终调用的destroy都是父类中的destroy,子类中的destroy是没有办法在析构的时候调用到的,因为构造函数和析构函数不能发生多态,不管直接调用虚函数还是间接调用虚函数,都是当前类中实现的虚函数

StaticLinkList<int, 5> list; //子类对象
for(int i=0;i<5;i++)
{
list.insert(0,i);
    }
for(int i=0;i<list.length();i++) //O(n)
{
cout<<list.get(i)<<endl;

}

main函数结束之后,对象被析构的时候,由于子类中没有实现析构函数,会调用默认析构函数,而默认析构函数会调用父类析构函数,调用父类析构函数会调用clear()函数,clear()函数会调用父类的destroy函数,delete pn; 直接delete,而对象由create虚函数创建,并且是子类中实现的,创建的空间是在m_space上的,最终父类destroy函数delete就不是堆空间了,这会造成程序不稳定,delete关键字只能释放堆空间,不是堆空间会造成程序奔溃。通过断点调试。在父类create和destroy函数的地方打上断点,在子类的create和destroy也打上断点,断点运行。首先调用子类create,然后调用子类的默认析构函数,而子类默认析构函数又会调用父类析构函数,继续调用父类clear,父类destroy,所以我们期望子类中destroy版本并没有调用到。子类create返回的内存空间被delete了,然而却不是堆空间。

改:在子类中添加自己的析构函数,调用clear函数,这会调用父类中的clear函数,而调用的destroy是当前类中的destroy,因为构造函数和析构函数不发生多态,在构造函数和析构函数中调用的虚函数就是当前类中的版本,不管是直接调用还是间接调用都是当前类中的版本。

~StaticLinkList()
{
this->clear();

}

6、WSlib中是否有必要增加多维数组类

没必要,多维数组的本质:数组的数组

#include <iostream>
#include "StaticLinkList.h"
#include "DynamicArray.h"
using namespace WSlib;
using namespace std;
int main()
{
DynamicArray<DynamicArray<int> > d; //d是一个对象,具体类型是一个数组,数组里边的元素是另一个数组DynamicArray<int>
d.resize(3);           //在DynamicArray构造函数中提供默认值0,动态数组对象如果不指明大小,默认大小为0,动态数组默认长度为0
for(int i=0;i<d.length();i++)
{
d[i].resize(3);
}
for(int i=0;i<d.length();i++)
{
for(int j=0;j<d[i].length();j++)
{
d[i][j]=i*j;
}
}
for(int i=0;i<d.length();i++)
{
for(int j=0;j<d[i].length();j++)
{
cout<<d[i][j]<< " ";
}
cout <<endl;
}
//不规则数组
DynamicArray<DynamicArray<int> > c; //d是一个对象,具体类型是一个数组,数组里边的元素是另一个数组DynamicArray<int>
c.resize(3);           //在DynamicArray构造函数中提供默认值0,动态数组对象如果不指明大小,默认大小为0,动态数组默认长度为0
for(int i=0;i<c.length();i++)
{
c[i].resize(i+1);
}
for(int i=0;i<c.length();i++)
{
for(int j=0;j<c[i].length();j++)
{
c[i][j]=i+j;
}
}
for(int i=0;i<c.length();i++)
{
for(int j=0;j<c[i].length();j++)
{
cout<<c[i][j]<< " ";
}
cout <<endl;
}
// 3*3二维数组
return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值