bobobobo
bobobobo

Reputation: 67224

Invoking a nonconst method on a member from a const method

I was surprised to find this "hole" in "const"ness:

#include <stdio.h>

class A
{
  int r ;
public:
  A():r(0){}

  void nonconst()
  {
    puts( "I am in ur nonconst method" ) ;
    r++;
  }
} ;

class B
{
  A a ;
  A* aPtr ;

public:
  B(){ aPtr = new A() ; }

  void go() const
  {
    //a.nonconst() ;      // illegal
    aPtr->nonconst() ;  //legal
  }
} ;

int main()
{
  B b ;
  b.go() ;
}

So basically from const method B::go(), you can invoke the non-const member function (aptly named nonconst()) if object of type A is referenced by a pointer.

Why is that? Seems like a problem (it kind of was in my code, where I found it.)

Upvotes: 8

Views: 474

Answers (3)

curiousguy
curiousguy

Reputation: 8270

Language definition of const

I was surprised to find this "hole" in "const"ness:

There is none.

const applies uniformly to all class members: in a const member function of class C, this has type const C *, so for a member C::mem declared as type T:

class C {
// ...
    T mem;
};

this->mem has type const T.

Please take type to discern what is the declared type T and the corresponding const-qualified type for all the members.

Seems like a problem (it kind of was in my code, where I found it.)

Just because the systematic application of rules does not do what you expected does not mean there is a problem with the rules, it means there is a problem with your expectations.

You should write down your expectations to see that you expected a non uniform application if const to different types.

When you program, you have to reason logically. You should infer things, not expect them when there is no logical reason.

Using const correctly

Why is that?

Your classes being called A and B, it's pretty hard to understand what constitute logical state and what does not. ;) You ask a "moral" question (not a question only on legal/illegal C++ programs), and your code fragment has no "moral" value. If you actually post relevant code, we might make some "moral" judgements about it.

Logical state

You should declare const the functions that do not change the "logical state" of the object it's applied to.

It means you have to define what is the "logical state" of your class instances: it's an abstract concept, and only you can define it, because it's a high level concept. The "logical state" relates to the problem your class should solve.

Then you can discern which variables contribute to the logical state: does *(b.aPtr) contribute to the logical state of b?

Closely related questions

Do you know about the copy constructor?

About the copy assignment operator?

About the destructor?

Upvotes: 0

Dave S
Dave S

Reputation: 21058

When and object of type B is const, then all of its members are const, which means its two members are, for the duration of B::go(), effectively

A const a;
A * const aPtr;

The first is a constant object of type A, on which you can only call const member functions. The second, however, is a constant pointer to a non-constant A. You could not legally say aPtr = <anything> from within the function B::go(), since that would modify aPtr, which is constant.

A pointer to a constant A would be declared as A const* aPtr or const A* aPtr, which would then make calling the non-constant A::nonconst() illegal.

Upvotes: 11

Greg Hewgill
Greg Hewgill

Reputation: 992857

The "const-ness" of an object does not extend to other objects through pointers. In your example, the const part is either the entire object a, or the pointer aPtr. Because aPtr is a A * and not a const A *, you are allowed to call a non-const method.

If you change

A* aPtr ;

to

const A* aPtr ;

then you will not be able to call aPtr->nonconst().

Upvotes: 6

Related Questions