Why do I keep getting unknown class error even though I have included them all in the header files?

Hello,

I have coded some classes for some computational exercises. But I cannot declare and assign an object of a derived class despite it being included in header files. Not really sure where I have made an error here.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cmath>
#include "AbstractOdeSolver.h"
#include "ForwardEulerSolver.h"
#include "RungeKutta.h"

class AbstractOdeSolver;
class ForwardEulerSolver;
class RungeKutta;

using namespace std;

int main()
{
	cout << "This program will solve an ODE" << endl;

	ForwardEulerSolver A;
	A.SetStepSize(0.1);
	A.SetInitialValue(2.0);
	A.SetTimeInterval(0.0, 1.0);
	A.viewParameters();
	A.SolveEquation();

	RungeKutta B;
	B.SetStepSize(0.1);
	B.SetInitialValue(2.0);
	B.SetTimeInterval(0.0, 1.0);
	B.viewParameters();
	b.SolveEquation();
	return 0;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
class AbstractOdeSolver;

using namespace std;

class AbstractOdeSolver
{
protected:
	double stepSize = 0.0;
	double initialTime = 0.0;
	double finalTime = 0.0;
	double initialValue = 0.0;

public:
	//Constructors to initialise or override values
	AbstractOdeSolver();
	AbstractOdeSolver(double, double, double, double);

	//Methods to set parameters
	void SetStepSize(double h);
	void SetTimeInterval(double t0, double t1); 
	void SetInitialValue(double y0);
	
	//Method to view parameters
	void viewParameters();
	
	//Virtual member functions to calculate values from ODE and solve equation
	//virtual double f(double y, double t) = 0; 
	virtual void SolveEquation() = 0;
	virtual double dy_dt(double) = 0;

};

AbstractOdeSolver::AbstractOdeSolver()
{
	double stepSize = 0.0;
	double initialTime = 0.0;
	double finalTime = 0.0;
	double initialValue = 0.0;
}

AbstractOdeSolver::AbstractOdeSolver(double h, double t0, double t1, double y0)
{
	stepSize = h;
	initialTime = t0;
	finalTime = t1;
	initialValue = y0;
}

void AbstractOdeSolver::SetStepSize(double h)
{
	stepSize = h;
	cout << "The step size has been set to " << stepSize << endl;
}

void AbstractOdeSolver::SetTimeInterval(double t0, double t1)
{
	initialTime = t0;
	finalTime = t1;
	cout << "The initial time has been set to " << initialTime << endl;
	cout << "The final time has been set to " << finalTime << endl;
}
void AbstractOdeSolver::SetInitialValue(double y0)
{
	initialValue = y0;
	cout << "The initial time has been set to " << initialValue << endl;
}

void AbstractOdeSolver::viewParameters()
{
	cout << "The parameters used to solve the equation are " << endl;
	cout << "Step size h = " << stepSize << endl;
	cout << "The initial time t0 = " << initialTime << endl;
	cout << "The final time t1 = " << finalTime << endl;
	cout << "The initial value y0 = " << initialValue << endl;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

//Include Guard
#ifndef __ABSTRACTODESOLVER_H_INCLUDED__
#define __ABSTRACTODESOLVER_H_INCLUDED__

//Forward declared dependencies
class AbstractOdeSolver;
class ForwardEulerSolver;

#include "AbstractOdeSolver.h"

using namespace std;

class ForwardEulerSolver : public virtual AbstractOdeSolver
{
public:
	const int n = floor((finalTime - initialTime) / stepSize);
	ForwardEulerSolver();
	ForwardEulerSolver(double, double, double, double);
	void SolveEquation();
	double dy_dt(double t);
	friend double f_actual(double);

private:
};

ForwardEulerSolver::ForwardEulerSolver()
{

}

ForwardEulerSolver::ForwardEulerSolver(double h, double t0, double t1, double y0)
{
	stepSize = h;
	initialTime = t0;
	finalTime = t1;
	initialValue = y0;
}

//To friend function to calculate the value of dy/dt = f(t,y)
double ForwardEulerSolver::dy_dt(double t)
{
	double f = 0.0;
	f = 1 + t;
	//cout << "1 + " << t << " = " << f << endl;
	return f;
}

double f_actual(double t)
{
	double f = 0.0;
	f = (pow(t,2) + (2 * t) + 4) / 2;

	return f;
}

void ForwardEulerSolver::SolveEquation()
{
	//Calculate number of steps to take
	int n = floor((finalTime - initialTime) / stepSize);

	//declare variables for ti, T0, i and h
	double T0, h, diff;
	const int ni = n;

	T0 = initialTime;
	h = stepSize;
	diff = 0.0;
	
	cout << "The number of steps to be take is = " << n << endl;

	//Set up output filestream
	ofstream write_output("data.txt");

	cout << " i " << setw(10) << " t " << setw(10) << " y " << setw(10) << " y_actual" << setw(10) << " Diff " << endl;
	cout << setprecision(4);
	
	double t[10];
	double y[10];

	y[0] = initialValue;
	t[0] = T0;

	for (int i = 1; i < n; i++)
	{
		t[i] = T0 + (i * h);
		y[i] = 0.0;
	}

	//Make a for loop for iterations using Euler's method
	for (int i = 1; i < n; i++)
	{
		y[i] = y[i - 1] + (h * dy_dt(t[i - 1]));

		//Set up output to screen
		cout			<< i << setw(10) << t[i] << setw(10) << y[i] << setw(10) << f_actual(t[i]) << endl;
		write_output	<< i << setw(10) << t[i] << setw(10) << y[i] << setw(10) << f_actual(t[i]) << endl;

		//Set yi and ti to T0 and y_i-1
		//T0 = ti;
		y[i - 1] = y[i];
	}
	write_output.close();
}

#endif // !__ABSTRACTODESOLVER_H_INCLUDED__ 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#pragma once
#ifndef __ABSTRACTODESOLVER_H_INCLUDED__
#define __ABSTRACTODESOLVER_H_INCLUDED__

#include "AbstractOdeSolver.h"
#include "ForwardEulerSolver.h"
#include "7.8_Exercises_7.3.cpp"

//Forward declared dependencies
class ForwardEulerSolver;
class AbstractOdeSolver;
class RungeKutta;

using namespace std;

class RungeKutta : public AbstractOdeSolver
{
public:
	RungeKutta();
	RungeKutta(double, double, double, double);
	const int n = floor((finalTime - initialTime) / stepSize);

	void SolveEquation();
	friend double f_actual(double);

private:
};

RungeKutta::RungeKutta()
{
}

RungeKutta::RungeKutta (double h, double t0, double t1, double y0)
{
	stepSize = h;
	initialTime = t0;
	finalTime = t1;
	initialValue = y0;
}

double f_actual(double t)
{
	double f = 0.0;
	f = (pow(t, 2) + (2 * t) + 4) / 2;

	return f;
}

void RungeKutta::SolveEquation()
{
	double k_1, k_2, k_3, k_4, h = 0.0;
	
	double t[10];
	double y[10];

	//Set step size
	h = stepSize;

	//Set initial values
	y[0] = 2.0;
	t[0] = 0.0;

	cout << " i " << setw(10) << " t " << setw(10) << " y " << setw(10) << " y_actual" << setw(10) << " Diff " << endl;

	for (int i = 1; i < n; i++)
	{
		t[i] = t[0] + (i * h);

		k_1 = h * dy_dt(t[i]);
		k_2 = h * dy_dt(t[i - 1] + (0.5 * h));
		k_3 = h * dy_dt(t[i - 1] + (0.5 * h));
		k_4 = h * dy_dt(t[i - 1]);

		y[i] = y[i - 1] + (1.0 / 6.0) * (k_1 + (2 * k_2) + (2 * k_3) + k_4); 

		cout << i << setw(10) << t[i] << setw(10) << y[i] << setw(10) << f_actual(t[i]) << endl; 

		y[i - 1] = y[i];
	}

}
#endif 
skeleton
1
2
3
4
5
6
7
//main.cpp
#include "ForwardEulerSolver.h" //provide the class definition of the objects you want to instantiate
#include "RungeKutta.h"
//no need to forward declare other classes, or the classes already defined
int main(){
   //...
}


1
2
3
4
5
6
7
8
9
10
11
12
//AbstractOdeSolver.h
//#pragma once //this have the same effect than the ifndef define combo, if your compiler support it
#ifndef ABSTRACTODESOLVER_H //this is a unique identifier
#define ABSTRACTODESOLVER_H
//no need to forward declare other classes, or the class we are about to define
//using namespece std; //this pollutes any source that includes this file
class AbstractOdeSolver{
	//...
};
//your member function implementation should be in another .cpp

#endif 

1
2
3
4
5
6
7
8
9
10
11
12
//AbstractOdeSolver.cpp
#include "AbstractOdeSolver.h"

#include <iostream>
using namespace std; //this is a source file, the pollution is contained here

//member function implementation
void AbstractOdeSolver::SetStepSize(double h)
{
	stepSize = h;
	cout << "The step size has been set to " << stepSize << endl;
}

1
2
3
4
5
6
7
8
9
10
11
//ForwardEulerSolver.h
#ifndef FORWARDEULERSOLVER_H //again, unique identifier
#define FORWARDEULERSOLVER_H
#include "AbstractOdeSolver.h" //we need the class definition to derive from it

class ForwardEulerSolver : public virtual AbstractOdeSolver{ //¿do you actually want virtual inheritance?
	//...
};

//again, may separate member function implementation (ForwardEulerSolver.cpp)
#endif 

1
2
3
4
5
6
7
8
9
10
11
12
13
//RungeKutta.h
#ifndef RUNGEKUTTA_H //again, unique identifier
#define RUNGEKUTTA_H
#include "AbstractOdeSolver.h" //we need the class definition to derive from it
//no need to even mention ForwardEulerSolver
//don't include .cpp, that may give you multiple definition errors
//http://www.cplusplus.com/forum/general/140198/

class RungeKutta : public AbstractOdeSolver{ //¿no virtual here?
	//...
};
//and yet again, may separate member function implementation (RungeKutta.cpp)
#endif 


if you have undefined reference link errors, read http://www.cplusplus.com/forum/general/113904/#msg622055
Last edited on
You have effectively coded up Simpson's rule for numerical integration, not a general Runge-Kutta solver. If you want to make it a general solver then dy_dt() needs to be a function of both t AND y.
@lastchance

dy/dt = 1 + t
The actual solution is t^2 / 2 + t + 2

I know that from my equations it does have f(y,t) but the only term is the t.
Last edited on
Mmm, but if dy/dt is just a function of t alone, then you simply solve it by integration. That rather limits the problems you could solve.

What happens if your problem had been
dy/dt = y + t
instead?
@lastchange

Yes, I think the point of the exercise was to have an analytical solution to check against. In the forward euler solver class I had also compared the values against the analytical one just to see it's degree of accuracy.
Hi all!

Thanks for your help I was able to sort the unknown class issues, but something else has cropped up. I will mark this as solved for now.
Topic archived. No new replies allowed.