【问题标题】:Information to store in exception object: providing strings in exception存储在异常对象中的信息:在异常中提供字符串
【发布时间】:2013-05-28 08:49:03
【问题描述】:

我想做类似的事情

FileIn::FileIn(const char* filename)
    {
    handle=CreateFile(filename,GENERIC_READ,FILE_SHARE_READ
        ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);

    if(handle==INVALID_HANDLE_VALUE)
        {
     // Base class ExceptionWinapi will call GetLastError();
        throw ExceptionWinapiFile(filename);
        }
    }

但是,如果 ExceptionWinapi 不复制文件名,则在捕获异常时它可能会无效。但是复制文件名需要 malloc(如果缓冲区不是固定长度),这可能会失败。那么字符串存放在哪里呢?

编辑:为了更清楚,请考虑

#include <cstdio>

class Foo
    {
    public:
        Foo(const Foo& foo){printf("Foo copy\n");}
        Foo(){printf("Foo\n");}
        ~Foo(){printf("~Foo\n");}
    };

class TestExcep
    {
public:
        TestExcep(const Foo& bar):m_bar(bar){}
    private:
        Foo m_bar;
};

class Test
    {
    public:
        Test(const Foo& bar)
            {throw TestExcep(bar);}
};

int main()
    {
    try
        {
        Foo bar;
        Test a(bar);
        }
    catch(const TestExcep& excep)
        {
        printf("Error\n");
        return 1;
        }
    return 0;
    }

打印(添加评论)

Foo
Foo copy
~Foo               <======Destroy old Foo after copy to the exception object!
Error
~Foo

编辑 2:如果 Foo(const Foo& foo){printf("Foo copy\n");} 抛出,那么就是那个异常不是旧的异常。这也很糟糕。

编辑 3:

ExceptionWinapiFile 的有用部分

ExceptionWinapiFile(const char* filename)
    {
    m_length=streln(filename)+1;
    m_buffer=(char*)malloc(m_length*sizeof(char));
    if(m_buffer==NULL)
        {
        //The problem
        abort(); //????
        }
    memcpy(m_buffer,filename,m_length*sizeof(char));
    }

还有(又是同样的问题)

ExceptionWinapiFile(const ExceptionWinapiFile& e)
    {
    m_length=e.m_length;
    m_buffer=(char*)malloc(m_length*sizeof(char));
    if(m_buffer==NULL)
        {
        //The problem
        abort(); //????
        }
    memcpy(m_buffer,e.filename,m_length*sizeof(char));
    }

至少在 dtor 中没有问题:

~ExceptionWinapiFile()
     {
     free(m_buffer);
     m_buffer=NULL; //As recommended by boost
     }

【问题讨论】:

  • 为什么不使用std::string 并在需要C 版本的字符串时调用它的.c_str() 方法。这样你就没有任何 malloc / 副本要处理。使用= 复制strings。
  • @Golgauth:如果异常对象采用字符串对象,则需要调用 std::string:s 复制构造函数,这可能会引发异常并...停止!如果它改为引用一个字符串(相当于只存储 char 指针),问题是字符串将在异常被捕获之前死亡。所以它并没有解决问题。
  • I've answered a similar question. 没问题,它会将filename 复制到异常对象中。
  • @M M. 当要创建要抛出的对象时会出现问题。如果不在堆(或其他地方)为文件名分配空间,我就无法创建它。

标签: c++ string exception memory-management


【解决方案1】:

您将不得不面对这样一个事实,即创建一个包含任意大小字符串的异常将不得不分配内存,这可能会失败。你唯一能做的就是缓解。

首先,可能发生的最糟糕的事情是 throw 子表达式成功,但是将异常复制到异常存储会抛出异常。在这种情况下, std::terminate 会立即被调用,你就完蛋了。你不想这样。

在实践中,这很少有问题,因为这个副本通常会被省略,这意味着没有机会抛出。此外,如果您使用的是 C++11,请确保您的异常具有无抛出移动构造函数。它将优先于复制构造函数使用,因此您不会冒异常的风险。

其次,throw 子表达式本身可能会抛出。如果是这样,那么将抛出新的异常而不是您想要的异常。在你的情况下,如果你在异常类中有一个 std::string 成员,你很可能会得到一个 std::bad_alloc 异常,而不是你想抛出的 ExceptionWinapiFile 。你必须问自己的问题是,这真的是个问题吗?如果您的内存太少以至于无法为文件名分配足够的空间,您真的还在乎文件没有被打开吗?无论如何,它很有可能因为内存不足而失败,如果它没有失败,那么你对文件所做的任何事情都可能由于内存不足而失败。

因此,除非您有非常非常具体的要求,否则我建议您不要担心。把 std::string 成员放在那里,你会没事的。

【讨论】:

  • 我完全同意。我们已经有了内存不足的异常 (std::bad_alloc)。其他异常类代表其他情况——无需担心内存(当然,在合理范围内)。
猜你喜欢
  • 2011-04-08
  • 2012-07-10
  • 1970-01-01
  • 1970-01-01
  • 2012-01-26
  • 2012-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多