扩展一个类

3

编辑回答:虽然我的原始问题没有像Konrad Rudolph所提供的答案那样精确地解释我的需求,但他(无论是意外还是故意地)基本上为我写下了我想要写的内容!该类本身并未得到扩展,但通过使类意识到新功能,可以扩展其功能,从而使其处理更广泛的问题。非常感谢出色的答案,这也是一个很好的教程,即使需要我自己轻松阅读书籍。

我相信这是一个非常基本的问题,但我正在尝试自学C ++,我使用的参考书提供了我不理解的答案。

为我的项目定义了一个class error_handler {},它处理来自程序各个点的异常消息和错误号码。 它将使用各种成员函数将错误记录到文件和/或将消息打印到屏幕上。

我的目标是创建它,使它拥有自己的.h和.cpp文件,如果以后需要扩展它,只需添加一个新的成员函数来处理某些更奇怪的类型的错误操作。

那么我的问题是:

由于在error.h中我有以下代码:

class error_handler
{
  // ctor dtor copy logger screen functions defined
}

error_handler.cpp中,我完成了这些定义,如何简单地向类中添加一个新函数? 我不想对其进行子类化,只是扩展它。
假设error_handler是在专有软件包中定义的类,在该软件包中,我无法直接修改源代码,但可以在单独的代码中扩展它。
编辑:迄今为止的答案和评论似乎表明我正在尝试做一些语言不应该做的事情。 如果是这样,那就这样吧,我在这里学到了一些东西。 希望这不是情况,我将保持问题开放一段时间,看看还有什么其他的可能会进入这个帖子......

2
通常(实际上也是唯一)扩展类的方法是“子类化”它(即从它派生)。“我不想子类化它,只是想扩展它。”听起来有点像“我想游泳但不想湿身”。 - Jerry Coffin
@JerryCoffin 嗯,有些(相当成熟和专业的)项目中使用了一些肮脏的宏黑客技巧。然而,除非你真的知道自己在做什么,否则你应该使用派生/继承机制,特别是在入门级别上。 - luk32
1
@NeonGlow,我至少可以想到三种不需要继承的方法。实际上,在这里,我最喜欢的选择可能不是继承。 - Konrad Rudolph
1
@Jerry Coffin -- 哈哈...不怕淋雨,只是在询问“是否可能”。我曾经涉猎过其他语言,其中类定义有一个“extends”修饰符。我希望这里也有类似的东西。 - Jase
@luk32 -- 正如你所说,我肯定是“入门级”的。因此,宏编程仍然是我的未来。 - Jase
@KonradRudolph -- 不涉及宏,您能否发布一个简单的示例? - Jase
3个回答

2
这在很大程度上取决于这些函数的具体内容。考虑到您的应用程序,我会假设您想添加的功能是处理其他错误类型的函数。
为了简单起见,让我们假设每个函数(我们称之为“handle”)只需传递一个错误对象,它将分析并处理或不处理该错误。它返回一个布尔值来表示处理结果。
然后您可以拥有以下(简化的)“error_handler”类:
class error_handler {
public:
    using handler_t = bool (*)(error const&);
    std::vector<handler_t> handlers;

    // …

    void add_handler(handler_t handler) {
        handlers.push_back(handler);
    }

    void handle_error(error const& error) {
        for (auto const& handler : handlers)
            if (handler(error))
                break;
    }
};

假设您的错误是由类error的实例表示的(并且您正在使用C++11-否则语法会稍有不同)。
现在,这个类定义是固定的。您可以通过调用add_handler来添加在其他文件中定义的其他处理程序。以下是两个示例:
// Simple logging “handler”. Needs to be added first!

extern error_handler global_error_handler;

bool log_handler(error const& error) {
    std::cerr << error.message() << '\n';
    return false; // We only log, we don’t handle.
}

global_error_handler.add_handler(log_handler);

// Handler for missing dog food

extern error_handler global_error_handler;

errno_t const MISSING_DOG_FOOD = 42;

bool dog_food_missing(error const& error) {
    if (error.code() != MISSING_DOG_FOOD)
        return false;

    global_dog_food_container.add(some_food());
    return true;
}

global_error_handler.add_handler(dog_food_missing);

哇!!这个例子比我预期的要好得多!你添加了一些我不熟悉的功能,所以我正在阅读更多资料。(特别是 using handler_t = bool (*)(error const&);,我从未在那种情况下看到过 using)感谢您提供如此详细的示例! - Jase
作为一个对C++11不太熟悉的新手,我对这段代码还有一些问题无法解决(尽管它运行得非常好)。我不想在SO上发布这些问题,请问您是否可以通过我的个人资料页面中的电子邮件与我联系?我将不胜感激。 - Jase
这是“命令链”模式吗?我很好奇,您提到的另外两种方式是什么? - Adri C.S.
1
@AdriC.S. 我必须承认我不知道那种模式。其他的方法是将类包装在另一个类中(装饰器模式),这在某种程度上是解决这个问题的“规范”解决方案,或者使用基于模板的解决方案,其中行为是通过策略而不是回调函数添加的(就像我的上述代码)。请注意,装饰器解决方案确实使用了继承——但它不是从 OP 的类继承,而是从一个共同的基类继承。 - Konrad Rudolph
@Jase 抱歉,我当时很忙,现在还是很忙,而且目前正在国外。不过,你可以自由地为我们创建一个聊天室,然后在那里提出你的问题。在评论区留下链接,我稍后会回复你。 - Konrad Rudolph
@KonradRudolph 谢谢你的信息。我用了错误的名称,应该是“责任链模式”。我最初在 OpenSceneGraph 的插件机制中看到它。 - Adri C.S.

2

C++中,如果你想修改一个类(如在error.h中),那么你需要修改该类的class块并添加函数定义。

如果你不能修改头文件,那么你只有两个选择:要么继承该类,要么写非成员函数来操作该类(但是这些函数只能访问公共成员)。


这就是我担心的事情。这也解释了为什么没有一本书提到这个概念。 - Jase
1
@Jase:这也说得通。如果您可以任意重写来自不同翻译单元的类型,那么您将能够引起各种混乱! - Lightness Races in Orbit
@LightnessRacesinOrbit -- 哈哈 -- 是的,它会!我同意(我的粗略理解)为什么不可能。这将引起各种问题。但是知识就是力量,我正在努力理解语言,而不是将其强制塑造成新的和模糊的形状和方向。感谢您再次指出这一点。虽然书中提到了这一点,但我直到现在才能够联系起来。 - Jase
@LightnessRacesinOrbit 这被称为猴子补丁,是其他语言中常见的概念。 - Konrad Rudolph
@Konrad:这就是我不使用那些语言的一个例子。 :) - Lightness Races in Orbit
@LightnessRacesinOrbit 我应该补充说:“……如果做得正确,它根本不会成为问题,而且可以成为一个非常有效的工具。” - Konrad Rudolph

1
创建一个派生类是扩展一个类而不修改其原始定义的唯一方法。也许你应该更具体地说明你想做什么。使用error_handler的现有代码是否必须受到你的更改的影响?例如,如果你只想添加一个以不同方式记录错误的函数,并且只打算在你的新代码中使用该函数,你可以创建一个接受error_handler引用的普通非成员函数,但更简洁的方法是创建一个名为my_error_handler的(派生)类,其中包含新方法。如果你希望将指向my_error_handler实例的指针/引用传递给现有代码,则可以自由转换为指向error_handler的指针/引用。

我还不确定如何转换指针/引用。我(也许有些愚蠢)在书中跳来跳去,试图通过重写非常古老的代码来学习新概念。(原始代码是C=64 basic) - Jase

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接