【问题标题】:Is it possible to use a pointer to an uninstantiated object as a variable in C++?是否可以使用指向未实例化对象的指针作为 C++ 中的变量?
【发布时间】:2020-01-02 18:06:25
【问题描述】:

我希望 C++ 类中的属性是来自特定类继承的未实例化类。然后,这个类 heirachy 的所有成员都将实现相同的方法,这意味着我可以实例化该对象,然后在情况需要时使用该方法。这是一些代码(不能编译),展示了我的意思:


#include <iostream>

using namespace std;

class Event {
public:
    Event() = default;
    virtual void go() = 0;
};

class EventA : Event {
public:
    EventA() = default;

    void go(){
        cout << "Running event A"<< endl;
    }
};

class EventB : Event {
public:
    EventB() = default;


    void go(){
        cout << "Running event B"<< endl;
    }
};

class Situation{
private:
    Event* current_event = &EventA;   //Problematic code: EventA does not refer to a value
public:
    Situation() = default;

    void setEvent(Event* event){
        current_event = event;
    }

    void runEvent(){
        current_event.go();
    }
};

int main() {

    Situation situation;
    situation.runEvent();
    situation.setEvent(&EventB);
    situation.runEvent();

    return 0;
};

【问题讨论】:

  • 您似乎对对象和类型之间的区别感到困惑。 C++ 类型不是对象,也没有地址。
  • 您可能想要创建一个EventA 的实例,它是类Situation 的静态成员,并将其命名为EventStub,然后设置current_event = &amp;EventStub;
  • 好的,这很有帮助。所以我不能像变量一样传递类以在以后实例化和使用。 @franji1 这适用于该代码的多态性方面吗?我可以使用static EventA eventStub; EventA* current_event = &amp;eventStub;,但不能使用Event* current_event = &amp;eventStub;
  • 另外,class EventB : Event私有继承,这意味着 EventB 类之外的任何代码都不允许知道 EventB 继承自 Event。使用class EventB : public Event

标签: c++ oop function-pointers


【解决方案1】:

不,你不能形成指向类的指针,你不能在没有类实例(对象)的情况下调用 [非静态] 成员函数。

您可能应该std::make_unique 一个您想要使用的类型的实例。

别忘了给你的基地一个虚拟析构函数,因为你在做多态的事情。

静态替代方案是std::variant

【讨论】:

    【解决方案2】:

    在两个地方,您似乎正在做可以描述为试图从类型中获取指针的事情:

    Event* current_event = &EventA;
    

    situation.setEvent(&EventB);
    

    这是行不通的,而且在 C++ 中并不是一个真正有意义的东西。您正在尝试做的事情可以通过我能想到的 3 种不同方式来实现。

    方法一:不用类,可以有一个函数指针,把函数指针作为参数传递:

    #include <iostream>
    
    using namespace std;
    
    void eventA_go(){
        cout << "Running event A"<< endl;
    }
    
    void eventB_go(){
        cout << "Running event B"<< endl;
    }
    
    class Situation{
    private:
        using EventFunctionPtr = void (*)();
        EventFunctionPtr current_event = &eventA_go;
    public:
        Situation() = default;
    
        void setEvent(EventFunctionPtr event){
            current_event = event;
        }
    
        void runEvent(){
            current_event();
        }
    };
    
    int main() {
    
        Situation situation;
        situation.runEvent();
        situation.setEvent(&eventB_go);
        situation.runEvent();
    
        return 0;
    };
    

    方法 2:您可以让这段代码更通用一点,通过在 Situation 类中允许任何类型的可调用对象,而不仅仅是函数指针:

    #include <iostream>
    #include <functional>
    
    using namespace std;
    
    void eventA_go(){
        cout << "Running event A"<< endl;
    }
    
    void eventB_go(){
        cout << "Running event B"<< endl;
    }
    
    class Situation{
    private:
        std::function<void ()> current_event = eventA_go;
    public:
        Situation() = default;
    
        template <typename F>
        void setEvent(F&& event){
            current_event = event;
        }
    
        void runEvent(){
            current_event();
        }
    };
    
    int main() {
    
        Situation situation;
        situation.runEvent();
        situation.setEvent(&eventB_go);
        situation.runEvent();
    
        return 0;
    };
    

    方法 3:您可以回到最初的想法,即必须实现一个基类以提供 go() 方法,但在这种情况下,您实际上必须确保您调用的对象确实存在。一种可能的方法是使用std::unique_ptr:

    #include <iostream>
    #include <memory>
    
    using namespace std;
    
    class Event {
    public:
        Event() = default;
        virtual ~Event() = default;
        virtual void go() = 0;
    };
    
    class EventA : public Event {
    public:
        EventA() = default;
    
        void go(){
            cout << "Running event A"<< endl;
        }
    };
    
    class EventB : public Event {
    public:
        EventB() = default;
    
        void go(){
            cout << "Running event B"<< endl;
        }
    };
    
    class Situation{
    private:
        std::unique_ptr<Event> current_event = std::make_unique<EventA>();
    public:
        Situation() = default;
    
        void setEvent(std::unique_ptr<Event>&& event){
            current_event = std::move(event);
        }
    
        void runEvent(){
            current_event->go();
        }
    };
    
    int main() {
    
        Situation situation;
        situation.runEvent();
        situation.setEvent(std::make_unique<EventB>());
        situation.runEvent();
    
        return 0;
    };
    

    注意,在这种情况下,抽象类的析构函数必须是virtual,并且继承必须是公共的。

    【讨论】:

    • 嗨@lvella,确实非常有用,谢谢。快速评论方法 1,大概也可以将 eventA_goeventB_go 分组到一个类中,然后传递 EventFunctionPtr = &lt;NameOfClass&gt;::eventA_go,而不是有很多顶级未分组函数?
    • 只有当它们是静态方法时,它们才不需要从公共基类继承。
    【解决方案3】:

    您似乎对类和变量感到困惑。 situation.runEvent(); 会在哪个对象上运行?我认为您希望公开Event 派生类并在需要时初始化current_event。你不需要像current_event = &amp;EventB 那样做任何事情。 C++ 根据current_event 动态指向的内容自动确定需要调用哪个函数。这就是我认为你的意图:

    #include <cassert>
    #include <iostream>
    
    class Event {
     public:
      virtual void go() = 0;
      virtual ~Event() = default;  // Don't forget the virtual destructor
    };
    
    class EventA : public Event {
     public:
      void go() override { std::cout << "Running event A" << std::endl; }
    };
    
    class EventB : public Event {
     public:
      void go() override { std::cout << "Running event B" << std::endl; }
    };
    
    class Situation {
     private:
      Event* current_event = nullptr;
    
     public:
      void setEvent(Event* event) { current_event = event; }
    
      void runEvent() {
        assert(current_event);
        current_event->go();
      }
    };
    
    int main() {
      Situation situation;
      EventA a;
      EventB b;
      situation.setEvent(&a);
      situation.runEvent();
      situation.setEvent(&b);
      situation.runEvent();
    }
    

    【讨论】:

    • 正确,我把事情复杂化了。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-17
    • 2012-04-26
    • 2011-10-01
    • 1970-01-01
    相关资源
    最近更新 更多