How can I address the multiple definition error in my derived classes from a abstract virtual one?

Hello,

I have made a program to calculate the numerical solution of an ODE. I am familiar with the technique to do so, but in a this task I have been set, I have to override the virtual function from the abstract base class and with a member function of the derived ForwardEulerSolver.

Unfortunately I receive the follwing errors.
"public: __thiscall FowardEulerSolver::FowardEulerSolver(void)" (??0FowardEulerSolver@@QAE@XZ) already defined in 7.8_Exercises_7.3.obj 7.8_Exercises_7.3

"public: __thiscall FowardEulerSolver::FowardEulerSolver(double,double,double,double)" (??0FowardEulerSolver@@QAE@NNNN@Z) already defined in 7.8_Exercises_7.3.obj 7.8_Exercises_7.3

"public: __thiscall AbstractOdeSolver::AbstractOdeSolver(void)" (??0AbstractOdeSolver@@QAE@XZ) already defined in 7.8_Exercises_7.3.obj 7.8_Exercises_7.3

"public: __thiscall AbstractOdeSolver::AbstractOdeSolver(void)" (??0AbstractOdeSolver@@QAE@XZ) already defined in 7.8_Exercises_7.3.obj 7.8_Exercises_7.3x
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>

using namespace std;

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

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

	A.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
#include <iostream>
#include <math.h>

using namespace std;

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

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;

};

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


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
#include "AbstractOdeSolver.h"

using namespace std;

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
#include "AbstractOdeSolver.h"
#include <iostream>

using namespace std;

class FowardEulerSolver : public virtual AbstractOdeSolver
{
public:
	FowardEulerSolver();
	FowardEulerSolver(double, double, double, double);
	void SolveEquation();
	friend double f(double y, double t);

private:
};

FowardEulerSolver::FowardEulerSolver()
{

}

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


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

#include "AbstractOdeSolver.h"
#include "FowardEulerSolver.h"
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cmath>

using namespace std;

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

void FowardEulerSolver::SolveEquation()
{
	//declare variables for ti, T0, i and h
	double ti, T0, h;

	ti = 0.0;
	T0 = initialTime;
	h = stepSize;

	//Declare values for yi, y_i-1, 
	double y_i = 0.0;
	double y_i_minus_1 = initialValue;

	//Calculate number of steps to take
	int n = floor ((finalTime - initialTime) / stepSize);
	cout << "The number of steps to be take is = " << n << endl;

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

	//Make a for loop for iterations using Euler's method
	for (int i = 0; i < n; i++)
	{
		//Calculate ti = T0 + ih
		ti = T0 + (i * h);

		//Calculate yi = y_i-1 + h * f(t_i-1, y_i-1)
		y_i = y_i_minus_1 + (h * f(y_i_minus_1, T0));
		cout << "t_i = " << ti << setw(5)<< "y_i" << y_i << endl;

		write_output << "t_i = " << ti << setw(5) << "y_i" << y_i << endl;

		//Set yi and ti to T0 and y_i-1
		T0 = ti;
		y_i_minus_1 = y_i;
	}
	write_output.close();
}
1. You don't seem to have inclusion guards. See http://www.cplusplus.com/articles/Gw6AC542/

2. Inplementations of
FowardEulerSolver::FowardEulerSolver() and
FowardEulerSolver::FowardEulerSolver(double,double,double,double)
are apparently in a header. That is an error even with guards.

3. How can your main() compile? You don't seem to include definition of class FowardEulerSolver in it.
Hello,

Based on your feedback and referral to the article, I have gone for the following organisation of the headers. It does compile woo hoo! Just need to check if it gives the right answers.

Could you check if my implementation of the include guards and forward declarations are correct.

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
//include Guards
#ifndef __ABSTRACTODESOLVER_H__
#ifndef __FORWARDEULERSOLVER_H__

#endif // !__FORWARDEULERSOLVER_H__

#include <iostream>
#include <fstream>
#include <iomanip>
#include "AbstractOdeSolver.h"
#include "FowardEulerSolver.h"

class AbstractOdeSolver;
class FowardEulerSolver;

using namespace std;

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

	FowardEulerSolver A;
	A.SetStepSize(0.01);
	A.SetInitialValue(2.0);
	A.SetTimeInterval(0.0, 1.0);
	A.viewParameters();

	A.SolveEquation();

	return 0;
}

#endif // !__ABSTRACTODESOLVER_H__ 


AbstractOdeSolver
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
//Should an include guard be included here?

// forward declared dependencies
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;

};

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


ForwardEulerSolver
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

//Include Guard
#ifndef __ABSTRACTODESOLVER_H_INCLUDED__
#define __ABSTRACTODESOLVER_H_INCLUDED__

//Forward declared dependencies
class AbstractOdeSolver;
class ForwardEulerSolver;

#include "AbstractOdeSolver.h"

using namespace std;

class FowardEulerSolver : public virtual AbstractOdeSolver
{
public:
	FowardEulerSolver();
	FowardEulerSolver(double, double, double, double);
	void SolveEquation();
	friend double f(double y, double t);

private:
};

FowardEulerSolver::FowardEulerSolver()
{

}

FowardEulerSolver::FowardEulerSolver(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 f(double y, double t)
{
	double f = 0.0;
	f = 1 + t;
	cout << "1 + " << t << " = " << f << endl;
	return f;
}

void FowardEulerSolver::SolveEquation()
{
	//declare variables for ti, T0, i and h
	double ti, T0, h;

	ti = 0.0;
	T0 = initialTime;
	h = stepSize;

	//Declare values for yi, y_i-1, 
	double y_i = 0.0;
	double y_i_minus_1 = initialValue;

	//Calculate number of steps to take
	int n = floor((finalTime - initialTime) / stepSize);
	cout << "The number of steps to be take is = " << n << endl;

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

	//Make a for loop for iterations using Euler's method
	for (int i = 0; i < n; i++)
	{
		//Calculate ti = T0 + ih
		ti = T0 + (i * h);

		//Calculate yi = y_i-1 + h * f(t_i-1, y_i-1)
		y_i = y_i_minus_1 + (h * f(y_i_minus_1, T0));
		cout << "t_i = " << ti << setw(5) << "y_i" << y_i << endl;

		write_output << "t_i = " << ti << setw(5) << "y_i" << y_i << endl;

		//Set yi and ti to T0 and y_i-1
		T0 = ti;
		y_i_minus_1 = y_i;
	}
	write_output.close();
}

#endif // !__ABSTRACTODESOLVER_H_INCLUDED__ 
If it compiles, it's only because you only have one compilation unit (where your main function is). If you were to use your FowardEulerSolver header or AbstractOdeSolver in more than one compilation unit (more than one ".cpp" file, handwaving some details), then you would get multiple definition errors. You can solve these errors by putting your class's function definitions in their own .cpp files, similar to how you originally were doing it, but constructor definitions also belong there, and you should have header guards as you do now. Or, mark all separated function definitions in the header as being inline, which allows the linker to assume the multiple definitions match.

Your cpp file that contains main should not have header guards.
This part of it doesn't even do anything:
1
2
3
#ifndef __FORWARDEULERSOLVER_H__

#endif // !__FORWARDEULERSOLVER_H__ 


//Should an include guard be included here?
Yes, you should put #include guards in any header file where it might be indirectly or directly #included more than once. It makes it more portable.

Also, just from a readability standpoint, you have a typo. Foward should be Forward.

Small note: Despite the fact that various tutorials may do it, you should not start your macros with two underscores or an underscore + capital letter. It's technically not allowed (reserved), although the chances of a naming conflict here happening are approximately 0.
Last edited on
Topic archived. No new replies allowed.