如何使用 C++ 中的模板编程从基类创建派生类?

Posted

技术标签:

【中文标题】如何使用 C++ 中的模板编程从基类创建派生类?【英文标题】:How to create derived classes from a base class using template programming in 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 或将参数传递给基类构造函数来设置字符串成员变量。 【参考方案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。

【讨论】:

以上是关于如何使用 C++ 中的模板编程从基类创建派生类?的主要内容,如果未能解决你的问题,请参考以下文章

如何使 Python 模拟从基类派生?

如何从基类动态创建和使用派生类?

如何从基类动态创建派生类

如何从基类调用派生类函数?

C++派生类是不是可以从基类继承静态数据成员和静态成员函数?

C++:: 指针 - 从基类转换为派生类