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