循环声明和循环引用
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;
}
我明白为什么这不起作用,我确实尝试过只声明指针而不是为 Mnozenje
和 Deljenje
初始化 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。
第二个是每次创建 Mnozenje
或 Deljene
时都会发生的分段错误:例如,如果您从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_ptr
和 weak_ptr
来帮助您管理内存;
- 或者仅在需要时创建反向操作(即不是在构造时,而是作为成员函数的结果);
- 或在您的数据结构中将操作与操作数分离;
- 或者通过添加一个
UndoOperation()
成员函数来避免完全创建反向操作。
我在处理这段计算数字运算的代码时遇到了问题。我尝试搜索,但似乎没有任何答案有帮助。
表示操作的基数 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;
}
我明白为什么这不起作用,我确实尝试过只声明指针而不是为 Mnozenje
和 Deljenje
初始化 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。
第二个是每次创建
Mnozenje
或Deljene
时都会发生的分段错误:例如,如果您从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_ptr
和weak_ptr
来帮助您管理内存; - 或者仅在需要时创建反向操作(即不是在构造时,而是作为成员函数的结果);
- 或在您的数据结构中将操作与操作数分离;
- 或者通过添加一个
UndoOperation()
成员函数来避免完全创建反向操作。