Introduction
Recently, I had a discussion with my friends about constant member functions in C++ (for example void MyClass::foo const {...}
)
And one of my friends (he is really great at C++) said that if a member function is const-qualified, then we cannot modify any member variables in the scope of this function.
Honestly, such a categorical opinion surprised me and I said, "it's not quite right!!" But all the participants in the debate supported my friend's view.
This discussion prompted me to write a short article on the topic about how easy it is to modify the data members of your class in a const function of C++
Background
In fact, even one of the most-read books about C++, called "The C++ Programming Language, Third Edition" by Bjarne Stroustrup, says the following:
"10.2.6 Constant Member Functions [class.constmem]
class Date
{
int d, m, y;
public :
int day () const { return d; }
int month () const { return m; }
int year () const ;
};
Note the const after the (empty) argument list in the function declarations. It indicates that these functions do not modify the state of a Date.
Naturally, the compiler will catch accidental attempts to violate this promise. For example:
inline int Date::year () const
{
return y ++; }
When a const member function is defined outside its class, the const suffix is required:
inline int Date::year () const
{
return y ; }
And really, if your code tries to change some data member in a const function (by the above manner), you would get an error. When you develop an application by using Visual C++, you will receive the following message:
error C3490: <your data member> cannot be modified because it is being accessed through a const object
I don't want to argue with the compiler (and even more so with the genial creator of C++) I only want to show another case ...
From my point of view, the author of the book didn't provide full disclosure of this subject, therefore, many programmers understand this subject insufficiently.
The Solution
I use a slightly different technique than the one showed by B.Stroustrup.
My approach here allows you to use an interesting trick. The trick may look quite ugly, but it works ...
class User
{
public:
User(unsigned int id, unsigned int age, std::string name)
: mID(id) ,
mAge(age),
mName(name)
{
}
void ChangeMe1 (unsigned int id, unsigned int age, std::string name) const;
void ChangeMe2 (unsigned int id, unsigned int age, std::string name) const;
void PrintMyData ();
private:
unsigned int mID;
unsigned int mAge;
std::string mName;
};
void User::ChangeMe1 (unsigned int id, unsigned int age, std::string name) const
{
User * synonym_of_this = const_cast<User *>(this);
synonym_of_this->mID = id;
synonym_of_this->mAge = age;
synonym_of_this->mName = name;
}
void User::ChangeMe2 (unsigned int id, unsigned int age, std::string name) const
{
const UserPtr &synonym_of_this = const_cast<User *>(this);
synonym_of_this->mID = id;
synonym_of_this->mAge = age;
synonym_of_this->mName = name;
}
void User::PrintMyData ()
{
std::cout << "ID = " << this->mID << " Age = " << this->mAge << " Name = " << this->mName.c_str() << "\n";
}
int main()
{
User presedent_USA (42,46,"Bill Clinton");
std::cout << "Now the presedent of USA : " << std::endl;
presedent_USA.PrintMyData();
std::cout << "\nLet's hold an election..." << std::endl;
presedent_USA.ChangeMe1(43,54,"George Walker Bush");
std::cout << "\nNow the presedent of USA : " << std::endl;
presedent_USA.PrintMyData();
std::cout << "\nLet's hold an election..." << std::endl;
presedent_USA.ChangeMe2(44,47,"Barack Hussein Obama 2");
std::cout << "\nNow the presedent of USA : " << std::endl;
presedent_USA.PrintMyData();
std::cout << "\n";
return 0;
}
As you can be see from the above code example we continue to use the const-qualified function, but after starting the following program:
We can see that all members of this class have been changed, i.e. the ChangeMe1
, and ChangeMe2
a "const" functions can modify any data members. Apparently the famous greek philosopher Heraclitus was right. In the ChangeMe1
function I used a "workaround" based on accessing data members through a pointer which is a synonym for this
. The second solution used in ChangeMe2
is based on creating a reference to this
, which behaves also as a synonym for this
.
Why is it so important to consider?
- The const function can change the data members of class
(for example, if you deal with scary legacy code and it's necessary to detect the place where some members of a class are changed, then do not ignore the code in the scope of a constant function, because there also may be changes) - If you want to change the data members of a class (for some reason), but it's not possible to change the signature of a method, then you can use the "trick" that I showed.
It is important to note that this should be done to resolve problems, but only in very extreme cases.
It basically doesn't make any sense to cancel or violate the constancy of constant method, but if you are going to use this "trick", please write detailed comments, to be able to maintain your code. That's all!
History
18nd April 2011: Initial post