(使用visual studio 2010)
我有一个简单的returnLength()
函数用于列表,简单定义为
int returnLength()
{
if (!next) return 1;
else return 1 + next->returnLength();
}
next是指向列表中另一个节点的指针。检查if语句以检查next是否有效时,Visual Studio会引发运行时错误,引用访问冲突。这个错误发生在递归树深处的几次调用中。
检查指针是否存在的推荐方法是什么?
答案 0 :(得分:4)
这是一种有效的方式。这里可能发生的是this
是NULL
,因此您尝试从next
指针读取NULL
时遇到访问冲突。您需要在NULL
returnLength
答案 1 :(得分:3)
您展示的代码有效。最有可能发生的是通过无效的this
指针调用此函数。
答案 2 :(得分:3)
听起来列表中的最后一个节点的next
值无效 - 它应该是NULL
。 next
在此类的默认构造函数中是否已明确初始化为NULL
?如果不是那么可能是问题。
答案 3 :(得分:2)
Visual Studio不会引发运行时错误,但我知道你的意思。
我们需要比这更多的代码。返回长度是您的班级列表的方法吗? next
在哪里宣布?它在哪里初始化?最有可能的是,next
是未初始化的指针或指向释放内存的指针。至少,它是指向无效位置的指针,因此是访问冲突错误。
适当地初始化next
。检查指针是否为空是指针有效性的唯一合适机制。
编辑:听起来你通常不熟悉指针有效性。您无法检查指针是否指向有效位置。您只能检查指针是否指向普遍无效的位置null
或0
。所以......如果你没有指向某个东西的指针,你应该初始化或设置为null
或0
,这就是它的目的。
答案 4 :(得分:1)
考虑到您的描述,next
似乎不为空,但仍然无效。例如。如果您的ListNode::RemoveNextNode()
函数有错误,它会删除下一个节点,但无法更改this->next
。在这种情况下,您有一个指向已释放内存的指针。你无法测试;你必须确保它不会发生。
一般来说,解决方案当然是使用std::list
。
答案 5 :(得分:1)
在C ++中,变量不能保证用合理的东西进行初始化。如果您创建一个变量: MyType * next;
next
指针可以是NULL
,也可以是任何其他值。此值将是垃圾,但不会为零,并且会导致您的访问冲突。
为了保证next
为NULL
,您必须确保在对象的构造函数中将其设置为NULL
。
有关详细信息,请参阅Variable initialization in C++。
答案 6 :(得分:0)
template <typename T_TYPE, typename T_RET, typename T_DELETE_POLICY>
class ToRef: public boost::tuple<T_TYPE*const&, T_RET, T_DELETE_POLICY>
{
public:
ToRef<T_TYPE, T_RET, T_DELETE_POLICY>(T_TYPE*const& p_type, const T_DELETE_POLICY delete_policy)
: boost::tuple<T_TYPE*const&, T_RET, T_DELETE_POLICY>(p_type, p_type!=NULL, delete_policy)
{
}
};
template <typename T_TYPE>
class ToRef<T_TYPE, bool, bool>: public boost::tuple<T_TYPE*const&, bool, bool>
{
public:
explicit ToRef<T_TYPE, bool, bool>(T_TYPE*const& p_type, const bool delete_policy)
:boost::tuple<T_TYPE*const&, bool, bool>(p_type, p_type!=NULL, delete_policy)
{
}
~ToRef()
{
//delete policy
if (get<2>())
{
if (NULL != get<0>())
delete get<0>();
const_cast<T_TYPE*&>(get<0>())= NULL;
}
}
private:
ToRef<T_TYPE, bool, bool>(ToRef<T_TYPE, bool, bool>& copy){};
ToRef<T_TYPE, bool, bool>& operator = (ToRef<T_TYPE, bool, bool>& rhs){};
public:
bool is_valid(void) const
{
//validity of the pointer.
return get<1>();
}
T_TYPE& r_get(void) const
{
if (is_valid())
return *get<0>();
throw std::string("Invalid Pointer");
}
protected:
T_TYPE*const & p_get(void) const
{
return get<0>();
}
};
//use it to safely access the pointers.
//if block is an overhead here.
#define safe_access_start(Ref) if (Ref.is_valid()) {
#define safe_access_end }
//faster mode but unsafe. exception handling takes care of preventing
//unhandled exception to cascade to the top.
#define fast_access_start try {
#define fast_access_end }catch (std::string s_exception){ TRACE("%s, %d,
%s", __FILE__, __LINE__, LPCTSTR(s_exception.c_str()));}
在你的情况下它可能会像这样工作
int returnLength()
{
if (!next) return 1;
sa::ToRef<Stest, bool, bool> testRef(sTest, false);
safe_access_start(testRef)
return 1 + testRef.r_get().returnLength();
safe_access_end
return 1;
}