将自定义异常 类 放在哪里?

Where to put the custom exception classes?

假设我有一个class,你可以在下面看到(也请阅读评论):

Foo.h

#pragma once

namespace Bar
{

class Foo
{
public:
    inline Foo( );

private:
    char c;
    inline static const std::unordered_set<char> CHAR_SET {'/', '\', '|', '-'};

    friend class Invalid_C_Exception;
};

} // end of namespace Bar

我有:

Foo.cpp

#include "Foo.h"

using namespace Bar;

inline Foo::Foo( )
{
}

class Invalid_C_Exception : public std::exception
{
public:
    virtual const char* what( ) const throw( )
    {
        std::stringstream ss;

        ss << "Invalid_C_Exception: { ";

        // compiler gives an error here because of usages of CHAR_SET
        for ( auto it = Foo::CHAR_SET.cbegin( ); it != Foo::CHAR_SET.cend( ); ++it )

        {
            ss << "'" << *it << "', ";
        }

        return somehow_save_the_C_string(ss.str());
    }

} Invalid_C_Exc;

第一个问题:

全局Invalid_C_Exc变量是否存在?

第二题:

为什么编译器在尝试编译 Foo.cpp 时指责我 CHAR_SET 是私有的?

自定义异常 class,尽管是 class Foo 的朋友,但无法访问 CHAR_SET?

第三题:

我应该把这些自定义异常 class 放在哪里?在单独的头文件和源文件中,但在相同的命名空间下?

我犹豫不决,所以我把声明和实现放在源文件中 Foo.cpp 因为异常 class 是为 class Foo 设计的,所以它们是相互关联的。

两者有很大区别

friend class Invalid_C_Exception;

friend Invalid_C_Exception;

后者期望 Invalid_C_Exception 已经存在 可访问的某个地方 并使该符号成为 Foo 的友元。前者在封闭范围 中声明了一个新符号 Invalid_C_Exception 并使其成为新朋友。

意味着你已经让 Bar::Invalid_C_Exception 成为 Foo 的朋友,而不是你稍后定义的全局 ::Invalid_C_Exception 仍然不是朋友。 Bar::Invalid_C_Exception 永远不会存在的事实无关紧要。

关于你的第一个问题,Invalid_C_Exc是一个全局变量,默认有外部链接。如果您仅在该翻译单元(.cpp 文件)中使用它,请将其设为 static 或将其放入匿名命名空间。 我建议不要偷懒,把它的定义写在单独的一行上,这样会更清楚。除此之外,你可以很好地使用它,它在 main 开始之前初始化并一直存在到程序结束。

关于你的第三个问题。无论你想要什么,我通常会建议每个异常类型一个 .hpp 或它们的紧密组合。将其放入 .cpp 中很难捕捉到它。

1st question: Is the existence of this variable ok?

全局变量通常不是一个好主意,并且由于异常 objects 在抛出时被复制到专用的异常存储中,我看不出这个特定的全局变量有任何用处。

second question is that why does the compiler blame me for CHAR_SET being private

定义一个classBar::Foo并且声明它的朋友classBar::Invalid_C_Exception ... 但是您根本没有触及实现文件中的那些。您刚刚在全局命名空间中声明了一个新的 和无关的 Invalid_C_Exception

删除 using namespace Bar 并将 namespace Bar { ... } 中包含项下方的所有内容包装起来,与 header 中的相同。或者,如果您愿意,可以在任何地方显式地写 Bar::FooBar::Invalid_C_Exception

using namespace 指令使命名空间 from 在封闭范围内可见。它不会将之后添加到封闭范围的所有内容 back 吸入命名空间。如果您有多个 using namespace 指令,那将如何工作?为什么 using namespace std; 会被允许?

3rd question: Where should I put these custom exception classes?

嗯,如果您希望任何人都能够使用它,则不能将定义放在您的实现文件中。把class的定义放在header里就行了,实现文件里只留下Bar::Invalid_C_Exception::what的定义。

他们是否进入相同 header & 实施文件作为他们的 class 朋友取决于你,它不是真的很重要。

4th question: how many questions should I ask per question?

一个。这叫问题,不是问卷。