【问题标题】:Where to put the custom exception classes?将自定义异常类放在哪里?
【发布时间】:2021-12-18 21:10:46
【问题描述】:

假设我有一个你可以在下面看到的课程(请也阅读 cmets):

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变量的存在好吗?

第二个问题:

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

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

第三个问题:

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

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

【问题讨论】:

  • 您真的需要自定义例外吗?有很多已经生成的异常类型 here 几乎涵盖了我遇到的大部分内容。
  • 代码是关于细节的。当某些行不相关时,您应该删除它而不是要求忽略它
  • @NathanOliver 自定义异常是一种很好的做法。在“清洁代码”中,Robert C. Martin 解释了原因(Java 被用作示例,但推理也适用于 C++)。

标签: c++ class exception header-files


【解决方案1】:

第一个问题:这个变量的存在好吗?

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

第二个问题是为什么编译器责备我 CHAR_SET 是私有的

您已定义一个类Bar::Foo 并声明它的朋友类Bar::Invalid_C_Exception ...但您在实现文件中根本没有触及这些。您刚刚在全局命名空间中声明了一个新的且不相关的Invalid_C_Exception。

删除using namespace Bar 并将包含在namespace Bar { ... } 中的所有内容都包含在与标题中相同的内容中。或者,如果您愿意,也可以在任何地方显式写入 Bar::Foo 和 Bar::Invalid_C_Exception。

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

第三个问题:我应该把这些自定义异常类放在哪里?

好吧,如果您希望任何人都能够使用它,您不能将定义放在您的实现文件中。只需将类定义放在头文件中,实现文件中只保留Bar::Invalid_C_Exception::what的定义即可。

他们是否在 same 头文件和实现文件中作为他们的朋友,这取决于你,这并不重要。

第四个问题:每个问题我应该问多少个问题?

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

【讨论】:

  • 我在 Foo.h 的命名空间 Bar 中也有一些静态 constexpr 变量。现在,如果我想将这些异常类放在单独的 .h 和 .cpp 文件中,那么它们的函数将无法访问 Foo.h 的那些文件静态变量。解决办法是什么?我是否应该将这些变量从 Foo.h 中取出并将它们放入 Foo.cpp 并使其成为全局变量,以便其他 .cpp 文件也可以访问它们?这是个好主意吗?
【解决方案2】:

区别很大

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 会让人难以理解。

【讨论】:

    猜你喜欢
    • 2012-08-31
    • 1970-01-01
    • 2011-11-30
    • 2012-04-16
    • 1970-01-01
    • 2010-12-27
    • 2021-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多