You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++:运行时通过基类指针识别派生类的实现疑问

Handling State Machine Signals with Derived Classes: TypeID vs. Best Practices

Hey there! Let's walk through your state machine design question—you're currently using function pointers for states, want to create separate classes for each signal, and are considering a typeid-based approach to let state functions handle specific signal types. Let's break this down.

Is using typeid a valid approach?

Short answer: Yes, it works, but it comes with tradeoffs that are worth weighing against your project's needs.

Using typeid to check the runtime type of a signal object lets you branch logic in your state functions like this:

void SomeState::handleSignal(Signal* signal) {
    if (typeid(*signal) == typeid(ButtonPressSignal)) {
        // Handle button press logic
    } else if (typeid(*signal) == typeid(TimeoutSignal)) {
        // Handle timeout logic
    }
    // ... other signals
}

This is straightforward to implement, especially if you have a small, fixed set of signal types that won't change often.

Why does the Signal class need a virtual function?

Great observation! typeid relies on Runtime Type Information (RTTI) to return the actual derived type of an object. For this to work correctly with a base class pointer/reference, the base class (Signal) must be polymorphic—meaning it has at least one virtual function.

If Signal has no virtual functions, typeid(*signal) will return the type of the base class (not the derived one) because the compiler can't track the runtime type without a vtable. The easiest way to fix this is to make the Signal destructor virtual (this also prevents memory leaks when deleting derived objects via base pointers):

class Signal {
public:
    virtual ~Signal() = default; // Makes Signal polymorphic
};

Alternatives to typeid (More OOP-Friendly)

While typeid works, it can violate the Open/Closed Principle—every time you add a new signal type, you have to go into every state function and add a new else if branch. For larger projects or ones where signals/states will evolve, the Visitor Pattern is a better fit.

Here's a quick outline of how it works:

  1. Define a StateVisitor interface with a visit method for each signal type.
  2. Make each Signal derived class implement an accept method that calls the corresponding visit method on the visitor.
  3. Each state class implements StateVisitor, providing the logic for handling each signal type in the matching visit method.

Example code snippet:

// Forward declarations
class ButtonPressSignal;
class TimeoutSignal;

class StateVisitor {
public:
    virtual void visit(ButtonPressSignal* signal) = 0;
    virtual void visit(TimeoutSignal* signal) = 0;
};

class Signal {
public:
    virtual ~Signal() = default;
    virtual void accept(StateVisitor* visitor) = 0;
};

class ButtonPressSignal : public Signal {
public:
    void accept(StateVisitor* visitor) override {
        visitor->visit(this);
    }
};

// Concrete state implementing the visitor
class IdleState : public StateVisitor {
public:
    void visit(ButtonPressSignal* signal) override {
        // Handle button press in idle state
    }
    void visit(TimeoutSignal* signal) override {
        // Handle timeout in idle state
    }
};

Now, instead of checking types in your state logic, you just call signal->accept(this), and the correct handler is invoked automatically. Adding a new signal only requires adding a new visit method to StateVisitor and implementing accept in the new signal class—no need to modify existing state code.

Which approach should you choose?

  • Use typeid if: You have a small number of signals/states, and the set is unlikely to grow. It's quick to implement and easy to understand.
  • Use the Visitor Pattern if: You expect to add new signals or states frequently, or want to adhere strictly to OOP design principles. It's more scalable but adds some initial boilerplate.

Either way, making your Signal class polymorphic (with a virtual destructor, at minimum) is a must if you're working with derived signal types via base pointers.

内容的提问来源于stack exchange,提问作者dr.umma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 06:57:08