循环声明和循环引用

Circular declarations and circular references

我在处理这段计算数字运算的代码时遇到了问题。我尝试搜索,但似乎没有任何答案有帮助。

表示操作的基数 class 是:

Operacija.h:

#pragma once
#include <iostream>
using namespace std;

class Operacija
{
protected:
    char* naziv;
    int drugiO;
    Operacija* suprotna;
public:
    Operacija* retOp() { return this->suprotna; }
    int retD() { return this->drugiO; }
    void printOp()
    {
        cout << "Ime operacije: " << this->naziv << endl;
        cout << "Drugi operand: " << this->drugiO << endl;
        cout << "Ime suprotne operacije: " << this->suprotna->naziv << endl;
    }
    virtual int DoOperation(int op1, int op2) = 0;
};

这个导出的class用于乘法:

Mnozenje.h:

//this is .h for 1st class
#pragma once
#include "Operacija.h"
#include "Deljenje.h"

class Mnozenje : public Operacija
{
public:
    Mnozenje(int b);
    int DoOperation(int op1, int op2);
};

Mnozenje.cpp:

#include "Deljenje.h"
#include "Mnozenje.h"
class Deljenje;
Mnozenje::Mnozenje(int b)
{
    Deljenje* a = new Deljenje(b);
    naziv = "Mnozenje";
    suprotna = a;
    if (b == 0)
    {
        cout << "operand ne sme biti 0, stoga je stavljen na ";
        cout << "default tj. postavljen na vrednost 1!" << endl;
        drugiO = 1;
    }
    else
        drugiO = b;
}

int Mnozenje::DoOperation(int op1, int op2)
{
    int a = 0;
    a = op1 * op2;
    return a;
}

而这个导出的 class 用于除法:

Deljenje.h:

#pragma once
#include "Operacija.h"
#include "Mnozenje.h"


class Deljenje : public Operacija
{
public:
    Deljenje(int a);
    int DoOperation(int op1, int op2);
};

Deljenje.cpp:

#include "Deljenje.h"
#include "Mnozenje.h"
class Mnozenje;  

Deljenje::Deljenje(int a)
{
    Mnozenje* b = new Mnozenje(a);
    naziv = "Deljenje";
    suprotna = b;
    if (a == 0)
    {
        cout << "operand ne sme biti 0, stoga je stavljen na default tj.     postavljen na vrednost 1!" << endl;
        drugiO = 1;
    }
    else
        drugiO = a;
}

int Deljenje::DoOperation(int op1, int op2)
{
    int a = 0;
    a = op1 / op2;
    return a;
}

我明白为什么这不起作用,我确实尝试过只声明指针而不是为 MnozenjeDeljenje 初始化 suprotna 成员。但是我有另一个 class 是 Operacija 类型数组的容器,我不能有未初始化的指针,因为我需要使用 suprotna.[=20= 调用其他函数]

您不需要 Deljenje.h 中的 #include "Mnozenje.h"Mnozenje.h 中的 #include "Deljenje.h"。您在 .cpp 文件中使用 class 的事实并不意味着您必须在 .hpp 文件中定义此 class。

您也不需要像这样编写 class 的声明:class Deljenje;,因为包含(例如 #include "Deljenje.h")为您提供了这些 classes.

似乎不​​太了解 include 是如何工作的。 这很简单。包括仅将整个指定文件复制到 #include 指令所在的位置。您可以在此处阅读更多相关信息:How does the compilation/linking process work?

你的问题提出了三个问题:

  • 首先是关于编译错误,由于不必要的循环引用。实现NO_NAME的答案,完全解决了这个题目demonstrated here

  • 第二个是每次创建 MnozenjeDeljene 时都会发生的分段错误:例如,如果您从int,然后您将在其构造开始时创建一个新对象 Deljenje,它将在其构造开始时创建一个新的 Mnozenje,它将创建 ...直到发生Whosebug。

  • 第三个问题是您打算使用继承。使用 Operacija 而不是 Operacija* 的容器将导致 slicing 并且注定要失败。

建议:

我了解到Mnozenje是乘法,Deljene是除法,您尝试将逆运算存储在suprotna中。

因此,我建议您预见一个带有可选参数的构造函数,这样当您创建反向操作时,您可以将原始操作指示为反向。

这个想法是这样的:

class Mnozenje : public Operacija
{
public:
    Mnozenje(int b, Operacija* rev=nullptr);
    int DoOperation(int op1, int op2);
};

Mnozenje::Mnozenje(int b, Operacija* rev)
{
    naziv = "Mnozenje";
    suprotna = rev==nullptr ? new Deljenje(b, this) : rev;   
    ...
}

// and the same kind of change for Deljenje

在这种情况下,不是永远递归,而是在相关对象之间创建循环引用。

注意: 此解决方法会使对象的销毁稍微复杂一些。如果考试时间充裕,可以考虑换个设计:

  • 保留两个指针用于反向操作:一个用于新分配的对象,一个用于指向原来的操作。在这种情况下,您可以进一步改进,使用 shared_ptrweak_ptr 来帮助您管理内存;
  • 或者仅在需要时创建反向操作(即不是在构造时,而是作为成员函数的结果);
  • 或在您的数据结构中将操作与操作数分离;
  • 或者通过添加一个 UndoOperation() 成员函数来避免完全创建反向操作。