Home > Software engineering >  C : how to define a class member with a reference
C : how to define a class member with a reference

Time:05-03

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

  1. 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)
}
}
  1. or have other variable members that could store value you give with roots.set_problem(problem).
  •  Tags:  
  • c
  • Related