是否可以将指向未实例化的对象的指针用作C++中的变量?

Is it possible to use a pointer to an uninstantiated object as a variable in C++?

本文关键字:指针 C++ 变量 对象 是否 实例化      更新时间:2023-10-16

我希望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;
};

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

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

不要忘记给你的基础一个虚拟析构函数,因为你正在做多态的事情。

静态替代方案是std::variant.

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

Event* current_event = &EventA;

situation.setEvent(&EventB);

这是行不通的,在C++中并不是一件真正具有正确含义的事情。您正在尝试做的事情可以通过我能想到的 3 种不同方式实现。

方法 1:可以有一个函数指针,而不是一个类,并将函数指针作为参数传递:

#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的,并且继承必须是公共的。

您似乎对类和变量感到困惑。situation.runEvent();哪个对象上运行?我认为您想Event公开派生类并在需要时初始化current_event。你不需要做任何像current_event = &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();
}