Why am I receiving these errors about multiple definitions?

Hi,

I am receiving the following errors regarding a virtual class with derived classes from it regarding multiple definitions.

Errors

LNK1169 one or more multiply defined symbols found 1

LNK2005 "double __cdecl f_actual(double)" (?f_actual@@YANN@Z) already defined in ForwardEulerSolver.obj RungeKutta.obj 1



Main
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.cpp"
#include "ForwardEulerSolver.h"
//#include "ForwardEulerSolver.cpp"
#include "RungeKutta.h"
//#include "RungeKutta.cpp"

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


AbstractOdeSolver.cpp
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
#pragma once

#ifndef ABSTRACTODESOLVER_H
#define ABSTRACTODESOLVER_H

/*1. Write the methods associated with the class AbstractOdeSolver and save these as the file AbstractOdeSolver.cpp.
Note that you do not have to write the pure virtual functions, as the “=0” when they are declared in the file
AbstractOdeSolver.hpp means that these are already written.*/

#include <iostream>

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;

	friend double f_actual(double);

};
#endif // !ABSTRACTODESOLVER_H 


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

AbstractOdeSolver::AbstractOdeSolver()
{
	stepSize = 0.0;
	initialTime = 0.0;
	finalTime = 0.0;
	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.h
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
#pragma once

#ifndef FORWARDEULERSOLVER_H
#define FORWARDEULERSOLVER_H


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

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:
};
#endif // !FORWARDEULERSOLVER_H 


ForwardEulerSolver.cpp
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
#pragma once

//#include "AbstractOdeSolver.h"
#include "ForwardEulerSolver.h"


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


RungeKutta.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#pragma once

#ifndef RUNGEKUTTA_H
#define RUNGEKUTTA_H

#include "AbstractOdeSolver.h"
#include <iomanip>

using namespace std;

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

private:
};

#endif // !RUNGEKUTTA_H 


RungeKutta.cpp
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

#pragma once

#include "RungeKutta.h"

RungeKutta::RungeKutta()
{

}

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

double RungeKutta::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 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];
	}
}
Last edited on
I am receiving the following errors

So post the errors.

BTW, #pragma once should not be used in source (cpp) files.
And you should NEVER say using namespace std at global scope in a header file.
Last edited on
@dutch

Doh! I just edited the first post with the specific errors I have

I have made those changes. What are the consequences of these
You've defined f_actual at global scope in two source files.
So you end up with two copies of it in the two object files that result from compiling those source files.
Then the linker complains when it tries to combine the object files together into the executable.

You could delete one of them and put a declaration for it in that file (since it needs the declaration to compile). You could also leave both of them but define them as inline:

1
2
3
4
5
inline double f_actual(double t)
{
    return (t * t + 2 * t + 4) / 2;
    // or return (t * (t + 2) + 4) / 2
}

The #pragma once simply doesn't do anything in a source file, since they are always only processed once. A header may be processed more than once, so it's useful there.

using namespace std in a header file forces the user of the header file to put up with the potential problems of dumping the entire std namespace into the global namespace. In general, you can decide to do that yourself in a source file, but by putting it in a header, you force anyone who uses the header to do it too, which may not be their desire.


Last edited on
Use either #pragma once or the macro-based inclusion guards, but not both; it's redundant.

Some projects avoid #pragma once
a.) because it is nonstandard (which is usually a silly reason: it works effectively everywhere); and more importantly
b.) because its exact semantics differ between systems based on the implementation's notion of file identity (i.e., how the implementation answers the question "are these two files the same").

Defining a notion of "file identity" is far outside the scope of the C++ standard, so #pragma once remains non-standard.
AFAIK this issue with #pragma once generally only occurs in corner cases - for instance, stuff involving filesystem links and exact copies of the same source files.

Trivially, include guards are more error-prone, but with the right static analysis tools, those errors can be avoided.
Last edited on
Hi,

So I have been able to get it to compile with accurate results. Here it is taking account of your feedback.

Main
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
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cmath>


#include "ForwardEulerSolver.h"

#include "RungeKutta.h"


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

//#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
#ifndef ABSTRACTODESOLVER_H
#define ABSTRACTODESOLVER_H

/*1. Write the methods associated with the class AbstractOdeSolver and save these as the file AbstractOdeSolver.cpp.
Note that you do not have to write the pure virtual functions, as the “=0” when they are declared in the file
AbstractOdeSolver.hpp means that these are already written.*/

#include <iostream>

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;

	//friend double f_actual(double);

};
#endif // !ABSTRACTODESOLVER_H 


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

AbstractOdeSolver::AbstractOdeSolver()
{
	stepSize = 0.0;
	initialTime = 0.0;
	finalTime = 0.0;
	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;
	std::cout << "The step size has been set to " << stepSize << std::endl;
}

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

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


ForwardEulerSolve.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef FORWARDEULERSOLVER_H
#define FORWARDEULERSOLVER_H

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

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);
	inline double f_actual(double);

private:
};
#endif // !FORWARDEULERSOLVER_H



ForwardEulerSolver.cpp

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 "ForwardEulerSolver.h"

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

inline double ForwardEulerSolver::f_actual(double)
{
	return 0.0;
}

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

	std::cout << "The number of steps to be take is = " << n << std::endl;

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

	std::cout << " i " << std::setw(10) << " t " << std::setw(10) << " y " << std::setw(10) << " y_actual" << std::setw(10) << " Diff " << std::endl;
	std::cout << std::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
		std::cout << i << std::setw(10) << t[i] << std::setw(10) << y[i] << std::setw(10) << f_actual(t[i]) << std::endl;
		
		write_output << i << std::setw(10) << t[i] << std::setw(10) << y[i] << std::setw(10) << f_actual(t[i]) << std::endl;

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


RungeKUtta.H

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef RUNGEKUTTA_H
#define RUNGEKUTTA_H

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

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

private:
};

#endif // !RUNGEKUTTA_H 


RungeKutta.cpp

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

RungeKutta::RungeKutta()
{

}

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

double RungeKutta::dy_dt(double t)
{
	double f = 0.0;
	f = 1 + t;
	//std::cout << "1 + " << t << " = " << f << endl;
	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;

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

	for (int i = 1; i < 10; 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);

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

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

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

	return f;
}
Hello,

I omitted the namespace std where I could and just added the std:: keyword to get around to have a more efficient use of resources. The inline keyword was actually one of the fixes suggested by VS 2019, but I was unsure as to its use. I have since implemented it and it compiles with expected results.

For both methods, RungeKutta and ForwardEuler cpp, I have this issue where I would like to use a variable n, for the limit variable in the for loops, but when doing so, I get an error saying that a non constant int can't be used, despite attempts to use the const keyword in the actual declaration in the body and in the corresponding header files.
What else are you using that n for? Are you using it to define the size of any arrays?
Hi Mikey boy

I just to use the n for the size of arrays also yes! When I did put it into the array size, then I was hit with the error. I had made it part of a formula and then declare it as an int. This you can see in ForwardEuler h in line 12.

It is the equation using step size and a subtraction

 
const int n = floor((finalTime - initialTime) / stepSize)
You can't use variables to define the size of an array in C++. You need to use a size that is known at compile-time - i.e. a constexpr.

If you don't know the size at compile-time, you're best off using a std::vector. However, if it's really important to you to use old-fashioned C-style arrays, then you'll need to dynamicallly allocate the memory.
Last edited on
Hi MikeyBoy,

I ideally want the program to respond to used input during run time and not to deal with it during the compile phase.
I could probably write it with array pointers and other related stuff. Maybe I will re-write with vectors as an addition.
Last edited on
If you want to size the array based on user input, you'll need to use one of the techniques I described. Presumably, by "array pointers and other related stuff", you mean dynamically allocating memory, yes?

EDIT: But I strongly advise you to learn how to use vectors for this sort of thing.
Last edited on
Hi MikeyBoy

Some time ago I had coded a program to calculate the exponent of a complex matrix. It was a total paint to do with the dynamic approach. I will try again with the vectors approach for sure.
Topic archived. No new replies allowed.