6

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?

Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
johnbakers
  • 24,158
  • 24
  • 130
  • 258
  • "the extra work...just seems like overkill, right?" No, it seems like the appropriate solution. – Caleb May 06 '13 at 12:18
  • 1
    There's a reason you don't let other objects do this, and a large part of that is to separate interface from implementation. A classes internals are private so they can change without breaking other parts of the application. – tadman May 06 '13 at 12:21
  • see [this related question](http://stackoverflow.com/q/14932675/819272) – TemplateRex May 06 '13 at 16:44

6 Answers6

6

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.

Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
  • Ok, understand, but what is the benefit of using getters in this case rather than just using the const pointers? Would seem to allow similar restrictions. – johnbakers May 06 '13 at 12:16
  • It is only the habit of promoting behaviour based, interface programming. Also it is also a way to follow the SOLID principle: Law of Demeter. http://en.wikipedia.org/wiki/Law_of_Demeter – Stephane Rolland May 06 '13 at 12:18
  • 4
    Why would you return pointers in your getters if no-one is supposed to assign to them anyway? Why not return a reference/reference to const? – pmr May 06 '13 at 12:19
  • Shouldn't the getter also be `const`? – tadman May 06 '13 at 12:20
  • @tadman yes that would be cleaner, I'm going to correct that. – Stephane Rolland May 06 '13 at 12:21
  • I meant `const` pointers, not methods that work on `const` references, but that's not a bad thing either. – tadman May 06 '13 at 12:25
  • to return a const pointer... I don't see what is the benefit... It impedes the user to reuse his pointer... as your question was "should..." my answer is traditionnally "no". – Stephane Rolland May 06 '13 at 12:27
  • main reason is - the client should not bother about your internals. By exposing the pointers, you expose too much. Additionally, adding error handling code will be difficult, you can only hope that the client checks for nullptr. With a getter, you could throw an appropriate exception. Or lazy initialization - with your approach not possible. Requirements change and nearly any change will enforce the client of your class to change the code. Try to minimize that by adding Getters (to const reference if possible) – Tobias Langner May 06 '13 at 12:57
  • @pmr I have added your proposal which is sound. Maybe even preferable in most cases. – Stephane Rolland May 06 '13 at 13:05
2

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.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • This answer is quite interesting in the general case, but since OP specified that access should be read-only, I guess usual invariant logic doesn't really apply. – André Caron May 19 '13 at 02:47
  • @AndréCaron: Maybe I was not clear enough. The fact that the two members have public and private interfaces does not mean that any operation applied to either of them will guarantee that the invariants of the complete type are maintained. The proposed solution has constant pointers to *mutable* objects, which means that the state of either object would be modifiable independently. The problem is not only pointing to a different object, but also that the state of the pointed object can change outside of the control of the complete type. – David Rodríguez - dribeas May 19 '13 at 02:54
  • Yes, I understood the first time. And I agree that the proposed code is wrong (showing "const pointer", rather than "pointer to const"), but OP has specifically stated *"I don't want users to change the actual objects that are enclosed into this composed parent class."* – André Caron May 21 '13 at 16:08
  • @AndréCaron: There are two interpretations of that sentence, you don't want to change the value of the objects or you don't want to change the identity of the objects. The OP seems to be quite aware of the semantics of `const` when applied to a pointer, so my interpretation is that he does not want users to change the *identity*, of the pointed objects, but understands that users can change the *value* they store. If the value can be changed, invariants **are** an important part of the decision. – David Rodríguez - dribeas May 21 '13 at 18:01
1

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.

Fabien
  • 12,486
  • 9
  • 44
  • 62
0

If you have a class that contains two other objects and provides free and full access to those members to its clients, then you should think about whether your class has the right design. The law of demeter doesn't become invalid because you don't use a getter, i.e. parent.ec1->foo is no better than parent.getEc1().foo.

In any case, const members are generally more trouble than they're worth.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
0

one of the reason could be that it could be transform to non const pointer by constcast.

0

Decide what your class is supposed to do, and write an interface that provides those things. If it's important to allow users to mess with internal data, then write interfaces to do that. However, in most cases, a class presents an abstraction that's different from the sum of the things you can do with its internal data, so the interface should not expose internal data. For example, a class that holds names of students will typically implement that as some sort of container that holds std::string data. That class should not provide a pointer or reference to the string for a person's name. Instead, if a name needs to be corrected, for example, that should be done in a separate editor class; once corrected, the stored name can be updated with the new version. There's no need for the class to expose the entire std::string interface to users.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165