【问题标题】:C++: Problems with inheritance and list functions as members of a classC++:继承和列表函数作为类成员的问题
【发布时间】:2018-11-22 23:45:23
【问题描述】:

这个问题在某种程度上是a question I have previously asked 的后续问题,因为我正在尝试理解和实施建议的概念。我已将所有内容重构为更小的类,并且武器的整体设计已经完成(并在一定程度上实现了)。但是,我在访问某些成员时遇到了问题,我相信一旦突破了这个障碍,我完成这个项目不会有任何问题,因为很多子类都使用类似的系统。

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

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();
    }
};

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

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*>; }
};

现在,它可以编译,但我不断收到“Factory Method.exe 中 0x0FC3CAB6 (ucrtbased.dll) 处的未处理异常:将无效参数传递给认为无效参数致命的函数。”错误。无论我在保持其整体功能的同时尝试对其进行多少修改,我似乎都无法使其正常工作,出现大量不同的错误。所以我想我应该向专家寻求帮助,因为我已经花了好几个小时把头撞在墙上试图跳过这个障碍。

【问题讨论】:

  • Wayyyy 在你的代码中有很多星星。你还在学习 C++:不要使用裸指针。尝试使用堆栈对象而不是指向动态分配对象的指针。如果需要,学习和使用参考资料。如果您真的需要指针,请了解智能指针。
  • 我假设这里存在大量内存泄漏:您几乎在任何地方都使用new 创建对象,而我看不到一个delete。原始指针应该保留给像容器一样在内部进行资源管理的低级类。所有其他类都应该依赖容器和智能指针。
  • 要了解您的错误,有必要查看运行上下文。但是关于代码的一些问题:为什么你使用那个接口:virtual list* ModeChoiceList() const?对于此函数的每次调用,您都会创建新的空列表,但我看不到您在哪里删除它。这种行为是你想要的吗?你可能需要这样的东西吗?类 A { list modeChoiceList;虚拟列表& ModeChoiceList() const { return modeChoiceList; } }
  • 每次调用Modes(),都会返回一个新的(并且是空的!)列表。所以每次调用it = Modes()-&gt;begin(); it != Modes()-&gt;end(); 都会将迭代器与两个不同的列表进行比较。
  • 您不需要单独的课程RailGunModeInformationobject ModeInformation railgun_modesMode railgun_mode_1;Mode railgun_mode_2; 构造而成。 ModeInformation 需要一个数据成员 list&lt;Mode&gt; m_modes不是返回new list&lt;Mode&gt;的成员函数,它是一个空列表

标签: c++ list class inheritance


【解决方案1】:

你的第一个问题是这样的

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()); }
}

如 cmets 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 (C++11) 替换 NULL

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

现在你会收到一个警告 bool 不是 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。

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

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

【讨论】:

  • 感谢您的帮助,但是,我仍然遇到了一些问题。我已经根据您的建议更改了 m_CreateModes() 和 m_CreateModeChoiceList() 函数,并相应地修复了生成的代码。但是,现在我遇到了这个问题:“ProgramName.exe 中 0x76F04A18 (ntdll.dll) 处的未处理异常: 0xC00000FD:堆栈溢出(参数:0x00000001、0x00802FA8)。”,在 RailGunModeInformation 类中,在以下行中:ModeList Modes() { ModeList ListMode; ListMode = m_CreateModes();返回列表模式; }
  • @S.Marques,现在是使用调试器的时候了,问题出在哪里会“很明显”。我猜你也会在另一个地方泄漏内存,考虑使用更少的“新”和更多的 std::vector,如果无法避免指针,请尝试使用 std::shared_ptr。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-07
  • 1970-01-01
  • 2010-09-08
  • 2017-01-05
  • 2013-02-11
  • 2020-07-17
  • 1970-01-01
相关资源
最近更新 更多