Const-correctness when passing complex types

So, I have four classes:

App is the entry point for the application.

MainPage is the main screen

Authenticator is an authentication helper / utility class

LoginPage is a login screen.

App, MainPage and LoginPage have pointers to Authenticator and are actually transferred from the application to MainPage, to LoginPage, when the user launches the application, reaches the main screen and the log is requested. The application creates MainPage, and if MainPage needs to log in, it creates LoginPage. The Authenticator pointer is passed at creation.

Let's say Authenticator looks something like this:

class Authenticator { public: std::string GetToken() const; void Login(std::string username, std::string pass); } 

Now the application will create a regular, non-constant pointer to the Authenticator, but since I do not want MainPage to modify the Authenticator, I want it to save the const pointer for it (i.e. it can only call const functions of the member on it). However, I would like LoginPage to call non-constant member functions like Login (), so when I pass my Authenticator from MainPage to LoginPage, I need to drop the constant.

My question is : is this bad in this situation? Should a class that does not have the right to modify an object pass it to one that can? Or would it be better if the application simultaneously created MainPage and LoginPage and gave them the same Authenticator to get started? My only problem with this option is that I am creating LoginPage actively, not lazily, and I would rather do it lazily.

Thanks in advance.

+4
source share
4 answers

You are missing an important part of the concept of a logical constant. When a class accepts a pointer (or link) to a const object, it promises to NEVER use a pointer / link so that it can modify the object. This, of course, means moving to someone else who could change it.

In other words, if MainPage plans to ask someone to change the Authenticator for it (that is, pass it a non-constant pointer to someone else), it is also responsible for the changes and should therefore need to store a pointer of a non-constant type.

+1
source

From an application point of view, MainPage modifies the authenticator. If it does directly or call the other side (LoginPage) to do this on behalf of, it does not matter. Therefore, MainPage must receive a non-contact pointer, and then pass it to the subpage to enter.

If you want your MainPage not to change the Authenticator, you can implement a base class for it that stores this pointer and has a method for calling the login dialog box. The authenticator is private, the method is protected. Then you can get your own MainPageDerived, which does not have (legal, non-hacker) ability to modify Authenticator, but can call LoginPage if necessary.

Notice what I said, because for 3 classes I would have thought it was too complicated. However, if you have more pages in the future, this may be an acceptable approach.

+2
source

From an interface point of view: if you have MainPage( Authenticator const* ) , you do not know that anything MainPage will modify the observed state of Authenticator . Directly or indirectly, if MainPage points to another class that will modify the object, you have broken the contract. Thus, in your case, this const-correctness requires MainPage( Authenticator* ) : MainPage code building does not care if the changes are direct or indirect; he just wants to know what a contract is and what it supports.

+1
source

Give MainPage just what he needs. You can look at it in several ways. This may be required:

  • An AuthenticationTokenSource that provides an updated Token .
  • An AuthenticatedExectuor that performs an Action that MainPage defines, but AuthenticatedExectuor provides authentication because it invokes an Action

There are probably other ways, but these are the first that spring is for the mind.

0
source

All Articles