C++:继承和列表函数作为类成员的问题

C++: Problems with inheritance and list functions as members of a class

本文关键字:成员 问题 函数 继承 列表 C++      更新时间:2023-10-16

这个问题在某种程度上是我之前提出的一个问题的后续问题,因为我正在尝试理解和实现所建议的概念。我已经将所有内容重构为更小的类,并且武器的整体设计已经完成(并有所实现(。但是,我在访问某些成员时遇到了麻烦,我相信一旦克服了这个障碍,我就不会有完成这个项目的问题,因为许多子类都使用类似的系统。

关于我的问题,我有一个父级的,部分虚拟的类,定义如下:

class ModeInformation {

public:
ModeInformation() { m_CreateModes(); m_CreateModeChoiceList(); } // These two functions are always called when an object belonging to the ModeInformation parent class is created, as they store elements in two lists, one in which the pointer-to-Modes themselves are stored, the other where Menu options are stored according to the number of modes
virtual ~ModeInformation() {}
// Modes of a Weapon (virtual as they might be overridden in child classes)
virtual Mode* pMode1() const { return NULL; }
virtual Mode* pMode2() const { return NULL; }
virtual Mode* pMode3() const { return NULL; }
virtual Mode* pMode4() const { return NULL; }
virtual Mode* pMode5() const { return NULL; }
// Lists mentioned earlier 
virtual list<string>* ModeChoiceList() const { return new list<string>; }
virtual list<Mode*>* Modes() const { return new list<Mode*>; };
// m_CreateModes() stores pointers-to-Mode in Modes(), if these are not NULL
void m_CreateModes() {
if (!pMode1() == NULL) { Modes()->push_back(pMode1()); }
if (!pMode2() == NULL) { Modes()->push_back(pMode2()); }
if (!pMode3() == NULL) { Modes()->push_back(pMode3()); }
if (!pMode4() == NULL) { Modes()->push_back(pMode4()); }
if (!pMode5() == NULL) { Modes()->push_back(pMode5()); }
}
// m_CreateModeChoiceList() stores strings in ModeChoiceList(), composed using the sstring library. Also a source of problems, as I will point out later on.
void m_CreateModeChoiceList() {
int i = 1;
for (list<Mode*>::iterator it = Modes()->begin(); it != Modes()->end(); it++) {
stringstream ChoiceDeclaration;
ChoiceDeclaration << "n" << i << ".- Mode " << i;
ModeChoiceList()->push_back(ChoiceDeclaration.str());
i++;
}
ModeChoiceList()->push_back("n0.- Quit to previous menu.");
}
// m_PrintBasicInfo() is called from classes that possess an object pertaining to the ModeInformation class (or derived child-classes), serves as a decision tree to judge whether the Mode List should be printed (in the case the Weapon being printed possesses more than one Mode), otherwise it will print the very first Mode
void m_PrintBasicInfo() {
if (Modes()->size() > 1) {
m_PrintModeList();
m_ChooseModeFromList();
}
else {
pMode1()->m_PrintBasicInfo(1);
}
}
// m_PrintModeList() prints each of the elements stored in ModeChoiceList()
void m_PrintModeList() {
list<string>::iterator it = ModeChoiceList()->begin();
while (it != ModeChoiceList()->end()) {
cout << *it << endl;
it++;
}
}
// m_ChooseModeFromList() provides a dynamic method for a user to choose which Mode's information will be printed
virtual void m_ChooseModeFromList() {
int Input = 0;
cout << "Please input your choice." << endl;
cin >> Input;
cout << endl;
list<Mode*>::iterator it = Modes()->begin();
switch (Input) {
case 1: if (it != Modes()->end()) { (*it)->m_PrintBasicInfo(Input); it++; break; }
else { m_ChooseInvalidModeFromList(); break; }
case 2: if (it != Modes()->end()) { (*it)->m_PrintBasicInfo(Input); it++; break; }
else { m_ChooseInvalidModeFromList(); break; }
case 3: if (it != Modes()->end()) { (*it)->m_PrintBasicInfo(Input); it++; break; }
else { m_ChooseInvalidModeFromList(); break; }
case 4: if (it != Modes()->end()) { (*it)->m_PrintBasicInfo(Input); it++; break; }
else { m_ChooseInvalidModeFromList(); break; }
case 5: if (it != Modes()->end()) { (*it)->m_PrintBasicInfo(Input); it++; break; }
else { m_ChooseInvalidModeFromList(); break; }
case 0: cout << "Returning to previous menu..." << endl; break;
default: m_ChooseInvalidModeFromList(); break;
}
}
// m_ChooseInvalidModeFromList() prints an invalid option message, and returns to m_ChooseModeFromList()
void m_ChooseInvalidModeFromList() {
cout << "Invalid option. Please choose a valid mode." << endl;
m_ChooseModeFromList();
}
};

我还有一个属于武器的示例子类,我用它来测试代码的可行性和执行。具体如下:

class RailGunModeInformation : public ModeInformation {
public:
Mode* pMode1() const { return new RailGunMode1(); }
Mode* pMode2() const { return new RailGunMode2(); }
list<string>* ModeChoiceList() const { return new list<string>; }
list<Mode*>* Modes() const { return new list<Mode*>; }
};

现在,它可以编译,但我不断收到"工厂方法中0x0FC3CAB6(ucrtbased.dll(未处理的异常.exe:将无效参数传递给将无效参数视为致命的函数。无论我如何尝试在保持其整体功能的同时对其进行返工,我似乎都无法让它工作,从而出现大量不同的错误。所以我想我应该向专家寻求帮助,因为我花了几个小时用头撞墙试图跳过这个障碍。

你的第一个问题是这个

void m_CreateModes() {
if (!pMode1() == NULL) { Modes()->push_back(pMode1()); }
if (!pMode2() == NULL) { Modes()->push_back(pMode2()); }
if (!pMode3() == NULL) { Modes()->push_back(pMode3()); }
if (!pMode4() == NULL) { Modes()->push_back(pMode4()); }
if (!pMode5() == NULL) { Modes()->push_back(pMode5()); }
}

如注释中所述,Modes(( 每次都返回一个新列表,但在下一个"}"之后无法访问puch_back的结果

我将只使用 C++11 来删除代码中一些更乏味的方面。

using ModeList = list<Mode*>;
ModeList m_CreateModes() {
ModeList modes = Modes();
if (!pMode1() == NULL) { modes->push_back(pMode1()); }
if (!pMode2() == NULL) { modes->push_back(pMode2()); }
if (!pMode3() == NULL) { modes->push_back(pMode3()); }
if (!pMode4() == NULL) { modes->push_back(pMode4()); }
if (!pMode5() == NULL) { modes->push_back(pMode5()); }
return modes;
}

现在模式一直存在,直到函数退出并返回,调用方现在必须存储返回的值,否则信息将丢失。或者,如果要重用值,则返回的模式可以是基类的成员变量。

其他问题,这可能会被误解

if (!pMode1() == NULL) { modes->push_back(pMode1()); }

假设 pMode1(( 返回 NULL,然后你得到 NULL 定义为 (0(

if (!NULL == NULL) { Modes()->push_back(pMode1()); }
if (true == 0) { Modes()->push_back(pMode1()); }

如果它返回的内容不是 NULL

if (!0xdeadbeef == NULL) { Modes()->push_back(pMode1()); }
if (false == 0) { Modes()->push_back(pMode1()); }

所以你真正想要的是,用 nullptr 替换 NULL(C++11(

if (!pMode1() == nullptr ) { modes->push_back(pMode1()); }

现在你收到一个警告,布尔值不是nullptr_t你应该写

if (!(pMode1() == nullptr) ) { modes->push_back(pMode1()); }

if (pMode1() != nullptr ) { modes->push_back(pMode1()); }

甚至

if (pMode1()) { modes->push_back(pMode1()); }

其他注意事项

m_ChooseModeFromList可能缺少循环。

重复使用相同的代码,只有一个数字不同,告诉我它应该是某种数组,std::array 或 std::vector。

使用原始指针通常是错误的,但并非总是如此。

一个好的自助是使用调试器单步执行代码。