Parent - Child Relation in C++

2.5k views Asked by At

Consider the below C++ code

class B;

class A{
private:
    B* mB;
};

class B{

private:
    doSomethingImportant();

};

We have a Object A that contains (has a) Object B. The parent being A and child being B. Now if I want A to make B do doSomethingImportant() , I see that adding A as a friend of B is the only way to do it.

friend class A inside class B. This would enable A's functions to access B's private function.

I find this approach a little weird since creates a loophole in the Data_Hiding concept. Is there a better way to establish a parent-child relationship between the object ? or is this the best way ?


Adding my actual motivation for this question

class elevator{
private:
    //The Lift box the elevator controls
    liftboxControlUnit & mLiftBoxCtrlUnit;   

    //constructor
    elevator(int Level=1, int NoOfBanks =1 );

    //Destructor
    ~elevator();

    //Triggers the search to move to the next floor if required 
    void moveLiftToNext();

public:

    //Adds request to the queue
    void addRequest(int FloorNumber){

    //Add the request to the queue. The single button outside the elevator door
    mLiftBoxCtrlUnit.addRequest(FloorNumber);

    }

    //For Emergency. Should be accessible to everyone !
    void setEmergency();
    void unsetEmergency();

};

typedef enum Direction{
    UP,
    DOWN
}direction;

class liftboxControlUnit{
private:

    //The request for various floors
    set<int> mRequestQueue;

    //The various banks for the whole system
    vector<Bank> mBanks;

    //The total number of levels. Remains the same for one building
    const int mTotalLevel;

    //Instruction to move the box to certain level
    void processRequest(){

        //Do the logic to move the box.

    }

    //can passed to the elevator
    void addRequest(int x){
        mRequestQueue.insert(x);
    }

    //Can be set by elevator class
    void setEmergency(){
        //Do the required 
        //Set Emergency on all Banks
    }

    void unsetEmergency(){
        //UnsetEmegency on all banks
    }

    void emergencyListener(){
        //Listen to all the banks if emergency has been set
    }

    void BankFreeListener(){
        //Listen to the banks if any is free

        //If so then
        processRequest();
    }

public:
    //Constructor
    liftboxControlUnit(int TotalLevels, int NoOfBanks): mTotalLevel(TotalLevels){
        for(int i=0 ; i lessthan NoOfBanks; ++ i)
            mBanks.push_back(Bank(0,UP));
    }    
    friend class elevator;
};

class Bank{
private:

    //The dailpad inside the bank
    dailpad & mpad;

    //Current Location
    int mPresentLevel;

    //Current direction of movement
    direction mDirection;

    //Currently moving
    bool mEngaged;

    //Manipulate the bank
    void move(int NoOfMoves){
        setEngaged();

        //Move the elevator

        unsetEngaged();    
    }

    //getters
    int getPresentLevel() const;
    int getDirection() const;

    //setters
    void setPresentLevel(int);
    void setDirection(direction);

    //Manipulate the engaged flag
    bool isEngaged() const;
    bool setEngaged();
    bool unsetEngaged();

    //For emergency
    void reset();

    //Dailpad Listener
    void dailpadListener(){

    }


public:
    Bank(int StartingLevel, direction Direction): mPresentLevel(StartingLevel),
            mDirection(Direction),
            mEngaged(false),
            mpad()
    {

    }

    //For emergency . Should be available for all.
    void SetEmergency();
    void UnsetEmergency();
    bool isEmergency();

    friend class liftboxControlUnit;
};


class dailpad{

private:
    //Some DS to represent the state . probably a 2D Array.

    void renderDisplay();

public:

    //Constructor
    dailpad();

    void getCommand(int x){
        //Depending on the value we can do the following

        //Make necessary changes to the display
        renderDisplay();
    }

    friend class Bank;
};
2

There are 2 answers

7
Jerry Coffin On BEST ANSWER

IMO, for this task you should probably nest the "lift box" class inside of the controller class:

class lift_controller { 

    class lift_box { 
        open_doors();
        close_doors();
        move_to_floor();
    };

    std::vector<lift_box> bank;
};

To the outside world, there need be no evidence that lift_box exists at all. It communicates exclusively with the lift_controller, and all outside communication with a lift_box goes through the lift_controller.

In this case (only lift_controller has access to lift_box at all), it seems clear (at least to me) that any operations the lift_controller may need to invoke on a lift_box should just be made public functions of lift_box. To enforce nobody else having access to lift_box, ensure that the definition of lift_box is in the private: section of lift_controller.

Edit: I should add that quite a bit of the design you've edited into your question above makes little or no sense to me. Just for example, you have things like direction and present level for the bank. Unless I'm completely misunderstanding what you mean by a bank, this seems like a clear error to me -- the bank isn't at a particular level or moving in a particular direction. Rather, each individual elevator in the bank is at some level and (potentially) moving in some direction.

0
devil On

You seem to want class A to only be able to access one private function in B, B::doSomethingImportant() and no other private functions.

This usually means that B::doSomethingImportant() should really be public. Like this, A will not be able to access other private data members of B.

Further, if you do not want other classes to access B::doSomethingImportant(), they should not hold a pointer to B but instead, a hold a pointer to an interface (abstract super class) of B that does not expose B::doSomethingImportant().

Or perhaps other classes only read data from B. In that case they can hold B const * which will not allow them to call B::doSomethingImportant() unless they do a const_cast.