随笔-90  评论-947  文章-0  trackbacks-0

先看一个例子。首先,我要写一个vector;其次,为了使用方便,我需要提供一个带 size 参数的构造函数。要求就这两点。

那么,势必要:

class vector
{
public:
    vector(size_t size)
    {
        // ...
        m_pData = new int[size]; // 假设就是 int 这样的基本类型好了,以避免下面可能出现的离题
        // ...
    }
};

问题来了。new 不是有可能失败吗?失败了在老编译器里会返回 NULL(这个情形也先无视),在新编译器里会抛异常。那么,在这里要不要进行检查呢?如果检查:

try
{
    m_pData = new int[size];
}
catch (...)
{

}

catch到了。那么在这里可以干啥呢?似乎。。。啥也干不了!作为构造函数,没法使用返回值,自然只能使用异常来提示外界;既然本来就是异常,我又何必在这里 try 一次呢?(假设这里没有其他错误要处理,也假设这里的类型是int之类的基本类型,不会出现执行元素的构造函数失败的情形)

既然这里的 try 让我们如此无奈,那么就不必 try 了。这个时候,我需要给 vector(size_t size) 标记上 throw 吗?如果不标记,使用者怎么知道这里可能会有异常?如果标记了,或者没标记但使用者意识到了,那么他会这样用:

try
{
    vector v(10);
    // Task with v
    // ...
    // ...
    // ...
}
catch (...)
{
    // Error handler
}

因为 v 的作用域被限制在了 try 内,所以所有的与 v 相关的逻辑代码全部要放在 try 内部了。这种样子似乎与 C# 很像!在 C# 里,try...catch... 是标准的做法;但是在 C++ 里,似乎不会如此经常地用 try catch,要不然,为什么我见过的 C++ 代码都不是这样子的呢?两年前在金山实习的时候,有一次我把 try...catch 当做通用的错误处理来做,所有的错误都搞成一种异常,返回值仅返回正常值。结果董波叔叔说,这样子是不对滴,但是没给出让我信服理由,可能就是,C++ 的 try...catch 的性能很不好之类的。(C# 以及 Java 的 try...catch 的性能好吗?)

好,既然大家都不这么办,是不是这里也不用 try 了?于是,内存分配错误就让它自生自灭了……记得以前某本书上看到,说这种情形下的处理,仅仅是一个道德问题而已。真的无解吗?

如果放宽要求,不要求在构造函数提供内存分配,那倒是有一种解法——分两阶段构造:

class vector
{
public:
    vector()
    {
        // ...
    }
    bool allocate(size_t size)
    {
        try
        {
            m_pData = new int[size];
        }
        catch (...)
        {
            return false;
        }
        if (m_pData == NULL)
        {
            return false;
        }
        // Other code ...
        return true;
    }
};

但是使用起来就不“方便”了。现实中,这种情形倒是存在,如 CWindow 的 Create,还有啥啥啥的 Init 等等。

真的没有办法兼顾方便与安全吗?

posted on 2010-03-30 22:31 溪流 阅读(2366) 评论(15)  编辑 收藏 引用 所属分类: C++

评论:
# re: 道德问题?论new操作失败后的操作 2010-03-30 22:48 | OwnWaterloo
最近很活跃嘛  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作 2010-03-30 22:54 | OwnWaterloo
>>我需要给 vector(size_t size) 标记上 throw 吗?如果不标记,使用者怎么知道这里可能会有异常?

不需要, 不标记就是throw all。

new失败了
1 :让bad_alloc直接向上抛就是了
为什么不需要大量的try catch?
因为向上抛的过程中会析构栈上的对象, 回滚状态, 并找到一个处理器。
你会将代码写成异常安全的, 是吧?

2: 采用两段式。
其实就是使用返回状态代码的处理方式了。

在某个函数f中, 先构造一个半成品, 在使用之前create或者怎样。
如果失败, 就通过状态码向f的调用者报告。
f的调用者g又可能向g的调用者继续报告。
直到找到一个能处理的地方。

这其实和异常是相同的, 只是异常对这些过程是自动的。
  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作 2010-03-31 00:14 | 溪流
@OwnWaterloo
其实我不是很清楚什么叫异常安全。不知道异常安全是尽量避免写出 try catch 还是尽量到处写 try catch。但我潜意识里不喜欢写 try catch,也不喜欢用会抛异常的东东,如 MFC 中的 CFile。  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作[未登录] 2010-03-31 08:55 | chentan
楼主最近写的都是非常敏感的话题  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作 2010-03-31 09:02 | 欣萌
Mark  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作 2010-03-31 09:14 | 溪流
@chentan
是吗?哈哈~  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作 2010-03-31 09:15 | 溪流
@OwnWaterloo
因为最近又想起了那些困惑的事~~  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作 2010-03-31 10:39 | ljbxc
我也常遇到,很烦人的问题。  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作 2010-03-31 14:52 | OwnWaterloo
@溪流
这样很好。 不要闷头只顾写代码; 花一些时间思考。  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作 2010-03-31 15:42 | 陈梓瀚(vczh)
你只要觉得,你那一行发生的错误的话那么你的class就不能被创建,那就在那里抛异常。这样可以使得你的class在那种情况下不可能被创建成功。  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作 2010-04-03 15:06 | 溪流
@陈梓瀚(vczh)
那我该期待别人怎么用我的class呢?  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作 2010-04-06 15:39 | only
你可以去认真看看《effective C++ 》(第三版) 条款49-52!
相信对于你会很有用,楼主!  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作 2010-04-06 15:54 | 溪流
@only
它是不是讲如何利用什么handler去释放内存之类的?
我的题设是内存申请失败,而不是被new的那个对象的构造函数执行失败。(我已经假设了被new的只是一个int)
我的困惑不是技术上如何保证没有内存泄漏,而是——
要是我的构造函数有异常抛出,用户该如何用这个类?我该不该让构造函数抛出异常?  回复  更多评论
  
# re: 道德问题?论new操作失败后的操作[未登录] 2010-04-14 15:11 | siwei
@溪流
有意思的话题。Symbian中的方式是重载了new,以及应用两段构造。  回复  更多评论
  

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理