Following my question, I coded a simple tool to compute a root using the bisection method, which is working fine. Here is the code:
roots.h
#ifndef ROOTS_H
#define ROOTS_H
class FunctRoots {
public:
// default constructor
FunctRoots();
// destructor
virtual ~FunctRoots();
// return the error
virtual double evaluate(double x);
};
class Roots{
private:
double tol=1e-5;
int max_iter=100;
double a, b;
double fa=0.0;
double fb=0.0;
public:
// default constructor
Roots();
// destructor
virtual ~Roots();
// set tolerance
void set_tolerance(double tolerance);
// set the search space
void set_search_space(double a, double b);
// bracketing
void bracketing(FunctRoots& problem, double start, double step);
// bisection method
double bisection(FunctRoots& problem);
};
#endif
roots.cpp
#include "roots.h"
#include "iostream"
#include "cmath"
// define the template for the function
FunctRoots::FunctRoots () {}
FunctRoots::~FunctRoots () {}
double FunctRoots::evaluate(double x){
return 0.0;
};
// Roots class
Roots::Roots() {}
Roots::~Roots() {}
// set search space
void Roots::set_search_space(double a, double b){
this->a = a;
this->b = b;
}
// set tolerance
void Roots::set_tolerance(double tolerance){
this->tol = tolerance;
}
// bracketing
void Roots::bracketing(FunctRoots& problem, double start, double step){
// set initial boundary
this->a = start;
this->fa = problem.evaluate(this->a);
// main loop
this->b = start;
for (int iter = 0; iter < max_iter; iter ) {
// update upper boundary
this->b = step;
this->fb = problem.evaluate(this->b);
// check if a root exists
if (this->fa*this->fb < 0) break;
// update lower bound
this->a = this->b;
this->fa = this->fb;
}
// check boundaries
if (this->a > this->b){
double temp;
temp = this->a;
this->a = this->b;
this->b = temp;
temp = this->fa;
this->fa = this->fb;
this->fb = temp;
}
}
// bisection method
double Roots::bisection(FunctRoots& problem){
// variables declaration
double fx, x;
// compute errors
if (fabs(this->fa) < 1e-12){
this->fa = problem.evaluate(this->a);
}
// main loop
x = 0;
for (int iter = 0; iter < max_iter; iter ) {
// compute solution
x = (a b)/2.0;
fx = problem.evaluate(x);
// print on screen
std::cout << "iter=" << iter << "\n";
std::cout << "a=" << a << "\n";
std::cout << "b=" << b << "\n";
std::cout << "x=" << x << "\n";
std::cout << "fx=" << fx << "\n\n";
// stop criterion
if (fabs(fx) < this->tol) break;
// update boundaries
if (this->fa*fx < 0){
this->b = x;
}else{
this->a = x;
this->fa = fx;
}
}
// function return
return x;
}
main.cpp
#include "roots.h"
#include "cmath"
class Problem: public FunctRoots{
private:
double value;
public:
Problem(double value){
this->value = value;
}
double evaluate(double x) {
return pow(cos(x),2) this->value-x;
}
};
int main(){
Problem problem(6);
Roots roots;
//roots.set_search_space(5, 10);
roots.set_tolerance(1e-7);
roots.bracketing(problem, 0,0.1);
roots.bisection(problem);
return 0;
}
Now, the question is this: how can I make my main
looks like this?
int main(){
Problem problem(6);
Roots roots;
roots.set_problem(problem) // <----NEW
//roots.set_search_space(5, 10);
roots.set_tolerance(1e-7);
roots.bracketing(0,0.1); // <---- problem is not here anymore
roots.bisection(); // <---- problem is not here anymore
return 0;
}
Basically, I would like to define my problem once and for all just after initializing the solver so that I don't need to give it as input to the functions anymore, however considering that the function evaluate
is defined in FunctRoots
but then overridden in Problem
.
CodePudding user response:
I would like to define my problem once and for all just after initializing the solver …
You can add a reference to a FunctRoots
object as a member of your Roots
class:
class Roots {
private:
FunctRoots& problem;
double tol = 1e-5;
//...
However, rather than initializing this "just after initializing the solver", you would need to intialize it at the time of creating that solver (i.e., pass it as a parameter to the constructor and initialize the member variable in an initializer list). So, your Roots
constructor would then look like this:
Roots::Roots(FunctRoots& fr) : problem{ fr } {}
You can then (as you desire) remove the reference to the Problem
object in your bracketing
and bisection
methods, and your main
would look like:
int main() {
Problem problem(6);
Roots roots(problem); // Initializes reference member at construction
roots.set_tolerance(1e-7);
roots.bracketing(0, 0.1); // <---- problem is not here anymore
roots.bisection(); // <---- problem is not here anymore
return 0;
}
Note that such reference members must be initialized at object construction time; they cannot then be reassigned to refer to different objects. Also note that polymorphism works with references, just as it does with pointers.
CodePudding user response:
How to define a class member with a reference
It's as simple as that, just declare a member reference and create a constructor to initialize it:
class Member{};
class MyClass{
Member& member;
public:
MyClass(Member& m) : member(m) {}
};
int main(){
Member m;
MyClass cl(m);
}
Note that the lifetime of Member
must be at least the same as MyClass
otherwise you will end up with a dangling referece.
CodePudding user response:
In this case, I think your root class should either
- have member variable "problem", and initialize it when you call roots.set_problem(problem).
like
class Roots {
private:
Problem *myProblem;
public:
void set_problem(Problem &problem){
myProblem = new Problem(problem.value)
}
}
- or have other variable members that could store value you give with roots.set_problem(problem).