在此代码的哪一行可能找到异常/错误?

时间:2018-12-06 02:01:51

标签: c++ error-handling exception-handling

这是代码:

template <typename T> class Ntuplet
{
    public:
        Ntuplet(std::initializer_list<T> s);
        ~Ntuplet(void);
        Ntuplet<T>& operator=(const Ntuplet<T>& t);
    private:
        size_t m_size;
        T* m_objects;

};

template<typename T>
    Ntuplet<T>& Ntuplet<T>::operator=(const Ntuplet<T>& t)
{
    if (&t == this)
        return *this;

    delete [] m_objects;
    m_size = t.m_size;
    m_objects = new T[t.m_size];
    for(int i = 0; i < m_size; ++i)
        m_objects[i] = t.m_objets[i];


    return *this;
}

这是来自旧考试。问题显示为:

” 在哪一行可能会引发异常;对象Ntuplet处于哪种状态(初始,相干,不相干,未定义)?提出一种更好的方法来实现该类,以避免出现异常/问题。”

我的猜测是在m_size = t.m_size上,因为我认为t.m_size的值可能太大,但这不可能,因为那样的话t对象将如何存在(该错误会更早出现)。唯一想到的另一件事是++i可能超出索引范围。.?

预先感谢

编辑:“相干”状态,表示对象处于没有矛盾属性的状态,但不处于我们想要的状态。

“不一致”表示属性不是应有的属性。例如,如果您执行a++ = b,但=运算符抛出错误,则a处于非相干状态,因为即使其余代码未得到被执行。在这种状态下,析构函数可用。

“未定义”与上面的相同,但是析构函数也不可用。

2 个答案:

答案 0 :(得分:0)

这里唯一可以抛出的代码是newstd::bad_allocT的构造函数抛出的东西)或值分配。

如果new抛出,则您的op=未完成,并且您的m_size将与m_objects不匹配(这将是悬而未决的)指向死内存的指针,该死内存用于保存可能具有其他大小的数组。

如果其中一项分配抛出,则您的op=不会完成,并且您的m_size将是正确的,但是某些(或全部)数组元素将是默认构造的,而不是具有您想要的值。

解决此问题的方法是使用向量,这样您就不必担心它。我并不是说要使用向量来实现Ntuplet……我的意思是用Ntuplet替换std::vector

答案 1 :(得分:0)

异常只能由非常特定的构造引发。诸如取消引用无效的指针,对C样式数组的超出范围的访问,类型转换中的未定义行为之类的未定义行为也不例外。

Ntuplet<T>::operator=(const Ntuplet<T>& t)中唯一可以引发异常的事物是new[]表达式(如果无法分配内存std::bad_allocT的默认构造函数抛出任何异常循环中使用的T类型的副本分配(取决于T是什么类型)。

如果new抛出异常,则m_objects将悬挂指针,因为它是事先delete[]插入的。 m_size将具有复制实例的大小。因此,对象将不会处于任何健全状态。 假设Ntuplet的析构函数实际上delete[]分配了为m_objects分配的内存,然后再调用它会由于double-free而导致未定义的行为。 如果您不打算用负责异常安全性的标准库实现替换Ntupletm_objects成员,那么针对此特定异常的一种解决方案是保存{的返回值修改任何成员之前,将{1}}表达式表示为临时指针new。然后,在删除旧的T* p之后,可以将临时文件分配给m_objects

如果循环中的赋值运算符引发异常,则实例也将处于部分分配状态。但是,随后调用析构函数应该很好(假设它仅删除m_objects),因为m_objects指向分配给m_objects的数组。要使此异常安全,需要将所有旧值保留在new中,因此循环应直接移至m_objects之后,并且应将其分配给new[]而不是p[]

但是这仍然会导致内存泄漏,因为如果分配循环抛出,m_objects[]分配的内存将不会被释放。因此,必须拦截任何异常才能删除new[]

p

我假设template<typename T> Ntuplet<T>& Ntuplet<T>::operator=(const Ntuplet<T>& t) { if (&t == this) return *this; T* p = new T[t.m_size]; try { for(size_t i = 0; i < t.m_size; ++i) p[i] = t.m_objects[i]; } catch (...) { delete[] p; throw; } delete [] m_objects; m_objects = p; m_size = t.m_size; return *this; } 的析构函数不会引发任何异常。原则上允许他们这样做,但这是不寻常的。