Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is encapsulation via public const pointers a good idea?

Tags:

c++

oop

So here is what I know:

  • It is wise not expose your ivars directly in your API; rather, use accessors
  • A const pointer to a non-const object simply means you can alter the object, but not redirect where the pointer points

Here is my situation:

I have a few related classes. I want to create a simple class that, via composition, combines these into one logical interface. Each of my enclosed classes already has a public and private distinction in its API so I do not mind exposing them directly to users of my parent class. This means that it would be overkill for me to write accessors for these ivars since the classes already manage what is public and what isn't. But, I don't want users to change the actual objects that are enclosed into this composed parent class.

So the only way I can think to do this is to use const pointers to these objects, as such:

class Parent{

  public:

   EnclosedClass1*const ec1; //users can enjoy the public API of EnclosedClass
   EnclosedClass2*const ec2;

   void convenienceMethod(){
         //performs inter-related functions on both ec1 and ec2
    }

 }

This way, there is no harm in letting someone directly interface with the API of ec1 and ec2 but, I want to make sure this is fairly foolproof in that at least that person cannot change the actual resource being manipulated, hence the const pointers.

Does this make sense, is it a good use of const pointers?

Alternately, I could make them private entirely, forget the pointers (this class manages these objects anyway), and just do the extra work to write accessors for the public functions contained in these objects. But that just seems overkill, right?

like image 511
johnbakers Avatar asked May 06 '13 12:05

johnbakers


3 Answers

The traditionnal OOP way is to keep data private, and have public accessors if one want to use them outside of the class.

If you don't want the private data pointers to be modified, then you don't provide setters, only the getters.

class Parent{

  private:
       EnclosedClass1*const ec1;
       EnclosedClass2*const ec2;

  public:
      EnclosedClass1* getEnclosedClass1() const {...};
      EnclosedClass2* getEnclosedClass2() const {...};    

      void convenienceMethod(){
    }

}

another possibility (like @pmr proposes in the comments below) is:

const EnclosedClass1& getEnclosedClass1() const {...};
const EnclosedClass2& getEnclosedClass2() const {...};    

with const reference you protect the user from testing the value returned by the getters. But you must ensure that your object state is consistent and never have internal pointers to nullptr; in such case you should throw an exception.

like image 90
Stephane Rolland Avatar answered Sep 20 '22 20:09

Stephane Rolland


There are different things wrong with this approach. The first one is that the fact that the members have a public and a private side to their interface, does not mean anything from the point of view of the enclosing type. The complete type can have a different set of invariants that users could break by independently modifying the two subobjects.

Even if the Parent type does not have any invariant (i.e. it does not depend on the state of the subobjects), the proposed approach is not const-correct. Given a const Parent& the caller can apply any operation (const and not const) to both subobjects, effectively changing the state of the Parent object. A slightly better approach in this case would be to store the subobjects directly:

class Parent {
public:
   EnclosedClass1 ec1;
   EnclosedClass2 ec2;
...

But I would recommend that you do provide accessors to extract the data and mutators (if the word makes sense) to change the state of the objects. That way if you need to add invariants later on you can just stick them there.

like image 38
David Rodríguez - dribeas Avatar answered Sep 21 '22 20:09

David Rodríguez - dribeas


Does this make sense ? Yes.

Is it a good use of const pointers ? Well, not really. The canonical way of coding is to have a getter function for each element and making internal data private. Doing another way will make readers of your code wonder why you're doing that since it is not standard practice.

So make your const pointers private and create getters to access them. For performance reasons, you can make these getters inlined functions, so that won't make any difference in the generated executable but will greatly enhance the readability of your code.

like image 45
Fabien Avatar answered Sep 21 '22 20:09

Fabien