【问题标题】:How can I get the same method code in a subclass without code duplication?如何在不重复代码的情况下在子类中获得相同的方法代码?
【发布时间】:2015-12-09 10:54:45
【问题描述】:

我有以下代码:

#include <exception>

class Exception : public std::exception {
private:
    const char* MESSAGE = "Exception"

public:
    inline virtual const char* what() const throw() {
        return this->MESSAGE;
    }
};

class ShoulderROMException : public Exception {
private:
    typedef Exception super;
    const char* MESSAGE = "ShoulderROM exception";

protected:
    static const int MAX_MESSAGE_LENGTH = 200;
    mutable char composedMessage[ShoulderROMException::MAX_MESSAGE_LENGTH];

public:
    virtual const char* what() const throw() {
        strcpy(this->composedMessage, super::what());
        strcat(this->composedMessage, " -> ");
        strcat(this->composedMessage, this->MESSAGE);
        return this->composedMessage;
    }
};

class KinectInitFailedException : public ShoulderROMException {
private:
    typedef ShoulderROMException super;
    const char* MESSAGE = "Kinect initialization failed."

public:
    virtual const char* what() const throw() {
        strcpy(this->composedMessage, super::what());
        strcat(this->composedMessage, " -> ");
        strcat(this->composedMessage, this->MESSAGE);
        return this->composedMessage;
    }
};

这会产生如下所示的日志条目: Exception -&gt; ShoulderROM exception -&gt; Kinect initialization failed. 这正是我想要的,但我想避免明显的代码重复,并且似乎找不到一种(n 优雅的)方法来做到这一点。

如果有人可以在这里帮助我,那就太好了。 :)

最好的问候, 莉萝

【问题讨论】:

  • 多次调用what 目前会以奇怪的方式失败。您是否考虑过使用嵌套异常?由于异常安全与内存分配,您是否使用字符数组而不是字符串?
  • 你考虑过boost.exceptions吗?
  • 是的,我认为 boost 和几个项目相关的原因不允许我使用它。另外,我不是 boost 的好朋友。我没有使用字符串,因为std::exception 强制使用char* 类型。
  • 我不太明白“std::exception 强制类型为 char*”是什么意思。我所知道的std::exceptionchar* 之间的唯一关系是std::exception::what,它返回一个char const*。但是可以将std::string 数据成员存储在std::exception 中,并使用std::string::c_strstd::exception::what 返回char const*std::string 的问题是动态内存分配,但这在实践中不一定是一个问题,有时可以通过使用 std::runtime_error 来避免。

标签: c++ exception duplicates code-duplication generalization


【解决方案1】:

通过一个通用类来实现它。我会像这样重写你的代码:

class Exception : public std::exception {
    static const char* MESSAGE = "Exception"
    static const int MAX_MESSAGE_LENGTH = 200;
    mutable char composedMessage[MAX_MESSAGE_LENGTH];

public:
    virtual const char* name() const throw() {
        return MESSAGE;
    }

    virtual const char* what() const throw() {
        strcpy(this->composedMessage, name());
        strcat(this->composedMessage, " -> ");
        strcat(this->composedMessage, this->MESSAGE);
        return this->composedMessage;
    }
};

class ShoulderROMException : public Exception {
    static const char* MESSAGE = "ShoulderROM exception";
public:    
    virtual const char* name() const throw() {
        return MESSAGE;
    }
};

class KinectInitFailedException : public ShoulderROMException {
    static const char* MESSAGE = "Kinect initialization failed."
public:
    virtual const char* name() const throw() {
        return MESSAGE;
    }
};

如果您不想在 Exception 类中实现这么多的实现,请添加另一个 ShoulderROMExceptionKinectInitFailedException 都将继承的类。

您的代码还有其他问题:MESSAGE 成员应该是static,而且您处理字符串的方式不是很C++ 风格。我还要补充一点,内联虚函数没有任何意义。

【讨论】:

  • 感谢您的建议,我喜欢它。将在接下来的几分钟内对其进行测试。 MESSAGE 应该是静态的是完全正确的,错过了那个。为什么内联虚函数没有意义?字符串处理是什么意思?如果您建议我应该使用std::string,那么我必须指出char* 是由std::exception 强制的。
  • 您的建议解决了直接从Exception 派生的所有类的问题,但不适用于从Exception 的子类派生的类。日志只获取来自Exception 的消息和来自Exception 的子类。所以在这个例子中,日志会说Exception -&gt; Kinect initialization failed.而不是Exception -&gt; ShoulderROM exception -&gt; Kinect initialization failed.。有没有办法在没有提到的代码重复的情况下实现这种递归?
  • 不要混淆:std::exception要求what返回char const *,但这并不意味着您不能使用std::string作为底层存储(听说过std::string::c_str()?)。此外,我错过了KinectInitFailedException 继承自ShoulderROMException 的事实,我会相应地更新我的答案。
  • 关于虚函数:对虚函数的调用意味着对虚表的查找,这是运行时多态性。内联函数是一个消失的函数,因为代码直接“粘贴”在你调用它的地方。这是编译时的选择。因此,不能内联虚函数。在您的情况下,您的编译器会忽略它。
【解决方案2】:

感谢您的所有帮助。它给了我灵感。有了一个同学的一些额外想法,我想出了这个效果很好的解决方案。 :)

#include <exception>

class Exception :
    public std::exception {
private:
    static const std::string MESSAGE = "Exception";

protected:
    std::string composedMessage;

public:
    Exception() :
    composedMessage(this->MESSAGE) {
    }

    virtual const char* what() const throw() {
        return this->composedMessage.c_str();
    }
};

class ShoulderROMException :
    public Exception {
private:
    static const std::string MESSAGE = "ShoulderROM exception";

public:
    ShoulderROMException() {
        this->appendMessage(this->MESSAGE);
    }

    virtual void appendMessage(std::string message) {
        this->composedMessage += " -> ";
        this->composedMessage += message;
    }
};

class KinectInitFailedException :
    public ShoulderROMException {
private:
    static const std::string MESSAGE = "Kinect initialization failed.";

public:
    KinectInitFailedException() {
        this->appendMessage(this->MESSAGE);
    }
};

我从错误的角度看待问题:自上而下而不是自下而上。 ^^

无论如何感谢您的帮助和最诚挚的问候, 莉萝

【讨论】:

  • 为什么不传入构造函数呢?在所有三种类型的构造函数中添加std::string参数,在每个构造函数中附加当前类型的消息并将结果传递给基类构造函数。
  • 这会使抛出异常变得不必要地复杂化,不是吗?
猜你喜欢
  • 2013-04-25
  • 2022-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-11
  • 2022-12-14
相关资源
最近更新 更多