1

I would like to do something like

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);
        }
    }

However, if the ExceptionWinapi does not copy filename, it will probably be invalid when it is time to catch the exception. But copy the filename would require a malloc (if the buffer is not fixed-length) which may fail. So where to store the string?

EDIT: To make it more clear, consider

#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;
    }

prints (comment added)

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

EDIT 2: if Foo(const Foo& foo){printf("Foo copy\n");} throws, then it is that exception that is cauch not the old one. This is also bad.

EDIT 3:

Useful parts of 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));
    }

Also (the same problem again)

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));
    }

No problem in dtor, at least:

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

1 回答 1

1

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

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

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

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

所以除非你有非常非常具体的要求,我说不要担心。把 std::string 成员放在那里,你会没事的。

于 2013-05-28T11:27:41.023 回答