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

标签 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处于不连贯状态,因为即使未执行其余代码,它也已递增。在这种状态下,析构函数可用。

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

最佳答案

异常只能由非常具体的构造引发。任何未定义行为,例如取消引用无效的指针,对C样式数组的超范围访问,类型转换中的未定义行为,都不异常(exception)。
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成员,则针对此特定异常的一种解决方案是在修改之前将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;
}

我假设T的析构函数不会引发任何异常。原则上允许他们这样做,但这是不寻常的。

关于c++ - 在此代码的哪一行可能找到异常/错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53643530/

相关文章:

c++ - 为什么我的代码中出现 'Runtime Error - SIGSEGV'?

c++ - 为什么 Rust 函数和 FFI C++ 函数以相反的顺序执行?

c++ - 删除/编辑 ATL COM DLL 属性/方法

reactjs - 如何处理 fetch() 中的错误请求

unit-testing - 单元测试中必须 "provoke"才能出错的东西的正确名称是什么?

design-patterns - 建议设计模式/方法公开/容忍/从系统错误中恢复,异常处理(Java,C++,Perl,PHP)

C++ 静态变量初始化是原子的吗?

python - 如何在 Gnome 终端中对 Python 错误的输出进行着色?

.net - 由于动态组装,Assembly.GetManifestResourceNames() 异常

java - 我们什么时候应该在方法中抛出异常或捕获异常?