【问题标题】:How to create derived classes from a base class using template programming in C++?如何使用 C++ 中的模板编程从基类创建派生类?
【发布时间】:2010-12-13 11:51:08
【问题描述】:

我需要从一个基类创建多个类(超过 50 个),唯一的区别在于派生类的名称。

例如,我的基类定义为:

class BaseError : public std::exception
{
private:
    int osErrorCode;
    const std::string errorMsg;

public:
    int ec;
    BaseError () : std::exception(), errorMsg() {}

    BaseError (int errorCode, int osErrCode, const std::string& msg)
         : std::exception(), errorMsg(msg)
    {
       ec = errorCode;
       osErrorCode = osErrCode;
    }

    BaseError (const BaseError& other)
        : std::exception(other), errorMsg(other.errorMsg)
    {
        ec  = other.errorCode;
        osErrorCode = other.osErrorCode;
    }

    const std::string& errorMessage() const { return errorMsg; }

    virtual ~BaseError() throw(){}

}

我必须从这个基类创建很多派生类,每个派生类都有自己的构造函数、复制构造函数和虚拟析构函数,目前我正在复制/粘贴代码,在必要时更改名称:

class FileError : public BaseError{
private:
    const std::string error_msg;

public:
    FileError () :BaseError(), error_msg() {}

    FileError (int errorCode, int osErrorCode, const std::string& errorMessage)
        :BaseError(errorCode, osErrorCode, errorMessage){}

    virtual ~FileError() throw(){}
};

问题: 有没有办法让这些类使用模板创建,这样就不会重复实现?

【问题讨论】:

  • 稍微不相关的评论:与其提供自己的const std::string& errorMessage() const getter,不如重新实现通过继承std::exception 获得的虚拟const char *std::exception::what() const 函数。
  • 为什么需要派生类?一个简单的typedef 还不够吗?据我所知,派生类没有任何用处。
  • 如果你的派生类的行为保持不变,那么为什么不去模板化类呢?
  • 另一个远程相关的评论:如果您的所有异常(可以)都有消息,请考虑在您的基类中只拥有一个 std::string errorMsg; 成员,而不是在基类中拥有一个成员,然后每个派生类中拥有一个成员.这更有效(就内存和运行时速度而言)。您可以通过提供(受保护的)setter 或将参数传递给基类构造函数来设置字符串成员变量。

标签: c++ templates derived-class


【解决方案1】:

我想你想创建一个类层次结构,这样你就可以在你的 catch 子句中使用动态调度(依靠编译器来找出正确的类型)来实现自定义错误处理。为此,您可以保持BaseError 类的原样,然后添加一个模板类,然后为其提供多个实例化。考虑一下:

class BaseError : public std::exception
{
private:
    int osErrorCode;
    const std::string errorMsg;

public:
    int ec;
    BaseError () : std::exception(), errorMsg() {}

    BaseError (int errorCode, int osErrCode, const std::string& msg)
         : std::exception(), errorMsg(msg)
    {
       ec = errorCode;
       osErrorCode = osErrCode;
    }

    // ...
};

template <int T>
class ConcreteError : public BaseError {
public:
    ConcreteError () :BaseError(), error_msg() {}

    ConcreteError (int errorCode, int osErrorCode, const std::string& errorMessage)
        :BaseError(errorCode, osErrorCode, errorMessage){}
};

您现在可以设置一些类型定义:

typedef ConcreteError<0> FileError;
typedef ConcreteError<1> NetworkError;
typedef ConcreteError<2> DatabaseError;
// ...

您现在有一个包含三个不同错误类别的层次结构。

【讨论】:

  • 我喜欢这个,非常优雅(尤其是你以后总是给特定的例外他们自己的实现而不破坏这个方案)。唯一要补充的是,整数值应该是命名常量或枚举的一部分。 :)
  • 这是否允许从共享库中抛出异常?
  • @Mephane:在这种情况下,命名常量或枚举会给你什么?您只需要有不同的类 - 您永远不会通过 ID 引用它们,因此您不需要为 ID 命名。
  • 好吧,您可以将枚举设为例如{ FILE, NETWORK, DATABASE, ... } 并将 ConcreteError 重命名为 Error,并跳过 typedef 以支持仅使用例如Error&lt;FILE&gt;。 :)
  • @Frerich Raabe:这确实是最优雅的解决方案。我只有一个问题,id 编号对于 typedef 来说应该是唯一的吗?或者,如果我对所有这些都使用相同的数字,会有什么不同吗?另外,非常感谢您详细查看我的代码。我一定会根据您的建议工作。
【解决方案2】:

如果实现相同,则创建一个枚举,并在其上进行模板化。

enum error {
    file_error,
};
template<error e> class my_exception : public BaseError {
    ....
};
typedef my_exception<file_error> file_exception;

【讨论】:

  • 为什么要在这种情况下使用命名常量或枚举呢?您只需要有不同的类 - 您永远不会通过 ID 引用它们,因此您不需要 ID 的名称。你总是只使用 typedef。
  • @Frerich:能够通过枚举引用它们并没有错。这是此解决方案的额外优势。
【解决方案3】:

如果实现完全相同,并且您只想为每个类使用不同的名称,那么简单的 typedef 就可以完成您的工作。

如果在实现上存在细微差别,但不是在接口上,那么您可能需要模板。然后再考虑policy based design

【讨论】:

    猜你喜欢
    • 2018-04-22
    • 1970-01-01
    • 2019-11-09
    • 1970-01-01
    • 2013-02-21
    • 1970-01-01
    • 2017-01-13
    • 2016-02-23
    • 1970-01-01
    相关资源
    最近更新 更多