Navigation



8.4 — Access functions and encapsulation

Access functions

An access function is a short public function whose job is to return the value of a private member variable. For example, in the above mentioned String class, you might see something like this:

class String
{
private:
    char *m_chString; // a dynamically allocated string
    int m_nLength; // the length of m_chString

public:
    int GetLength() { return m_nLength; }
};

GetLength() is an access function that simply returns the value of m_nLength.

Access functions typically come in two flavors: getters and setters. Getters are functions that simply return the value of a private member variable. Setters are functions that simply set the value of a private member variable.

Here’s an example class that has some getters and setters:

class Date
{
private:
    int m_nMonth;
    int m_nDay;
    int m_nYear;

public:
    // Getters
    int GetMonth() { return m_nMonth; }
    int GetDay() { return m_nDay; }
    int GetYear() { return m_nYear; }

    // Setters
    void SetMonth(int nMonth) { m_nMonth = nMonth; }
    void SetDay(int nDay) { m_nDay = nDay; }
    void SetYear(int nYear) { m_nYear = nYear; }
};

Why bother to make a member variable private if we’re going to provide public access functions to it? The answer is: “encapsulation”.

Encapsulation

In real life, it is common to use something without knowing how it actually works. For example, your TV remote provides buttons that allow you to do things like turn your TV on and off and adjust the volume. However, the details of how the remote is actually implemented is hidden away. This is useful because it allows you to use the remote without having to worry about the details of why it works or how it was implemented. If gnomes broke into your house in the middle of the night and replaced the internals of your TV remote with a new (but compatible) technology, you’d probably never even notice.

Encapsulation is the idea of hiding the details of how something is implemented and instead exposing an interface to the user. This allows the user to use the item without having to worry about how it is implemented.

In C++, access specifiers allow us to implement encapsulation within our classes. This is typically done by making ALL member variables of a class private, and providing public functions (often access functions) that allow the user to work with the class. Although this may seem more burdensome than providing public access directly, doing so actually provides several very useful benefits that help encourage class reusability and maintainability.

Perhaps most importantly, sometimes it turns out that the initial implementation of a class is too slow or uses too much memory, and a more complex solution is needed. Encapsulating the implementation means that the implementation of a class can be completely changed, and so long as the interface remains the same, the users of the class do not have to worry about the changes at all!

Consider this simple example:

class Change
{
public:
    int m_nValue;
};

int main()
{
    Change cChange;
    cChange.m_nValue = 5;
    std::cout << cChange.m_nValue << std::endl;
};

While this program works fine, what would happen if we decided to rename m_nValue? We’d also break our program! Encapsulation gives us the ability to change our classes without breaking all the code that uses them.

Here is the encapsulated version of this class that uses access functions to access m_nValue:

class Change
{
private:
    int m_nValue;

public:
    void SetValue(int nValue) { m_nValue = nValue; }
    int GetValue() { return m_nValue; }
};

int main()
{
    Change cChange;
    cChange.SetValue(5);
    std::cout << cChange.GetValue() << std::endl;
}

Now when we decide to rename m_nValue, we only need to change SetValue and GetValue() to reflect the change. Our program does not need to be changed at all!

Second, hiding the details about how a class is implemented means a programmer can use the class without knowing how it was implemented. This lowers the time needed to learn how to use a class, and makes the class much easier to work with.

Third, encapsulation helps prevent accidental changes and misuse. Because the member variables can not be accessed directly, this helps prevent inadvertent changing of values. Furthermore, it is often the case that when a value is modified, other values also need to be updated. For example, in a typical String class, when the string is modified, the length also needs to be updated. If the user has direct access to the string, he/she may forget to update the length when the string is changed. However, an interface function that allows the user to change the string can automatically update the length whenever the string is changed, meaning the user doesn’t even have to worry about it!

And finally, encapsulation helps you debug the program when something goes wrong. Often when a program does not work correctly, it is because one of our member variables has an incorrect value. If everyone is able to access the variable directly, tracking down which piece of code modified the variable can be difficult. However, if everybody has to call the same function to modify a variable, you can simply breakpoint that function and watch as each caller changes the value until you see where it goes wrong.

As you can see, encapsulation provides a lot of benefits for just a little bit of effort. In particular, the ability to change the implementation details of the class without affecting any of the programs that use the class is paramount to code maintainability!

8.5 — Constructors
Index
8.3 — Public vs private access specifiers

25 comments to 8.4 — Access functions and encapsulation

  • [...] 2007 Prev/Next Posts « 8.2 — Classes and class members | Home | 8.4 — Access functions and encapsulation » Tuesday, September 4th, 2007 at 2:17 [...]

  • Jeffey

    on your last 2 examples there is an error.

    std::cout << cChange.GetValue() << endl;

    needs to be changed to:

    std::cout << cChange.GetValue() << std::endl;

    though im sure most can figure it out this late in the tutorial.

    [ Fixed. Thanks! -Alex ]

    • Tom

      When I try the “fixed example”

      class Change
      {
      private:
          int m_nValue;
      
      public:
          void SetValue(int nValue) { m_nValue = nValue; }
          int GetValue() { return m_nValue; }
      };
      
      int main()
      {
          Change cChange;
          cChange.SetValue(5);
          std::cout << cChange.GetValue() << std::endl;
      }
      

      I get the errors:

      g++ j39.cpp -g -o xxx
      j39.cpp: In function ‘int main()’:
      j39.cpp:15: error: ‘cout’ is not a member of ‘std’
      j39.cpp:15: error: ‘endl’ is not a member of ‘std’

  • [...] 2007 Prev/Next Posts « 8.4 — Access functions and encapsulation | Home | 8.6 — Destructors » Wednesday, September 5th, 2007 at 3:10 [...]

  • [...] Learn C 8 4 Access functions and encapsulation Posted by root 2 hours 15 minutes ago (http://www.learncpp.com) An access function is a short public function whose job is to allow you to do things like turn your tv on and off and adjust the volume you can edit your comment after posting by clicking on it learn c is powered by wordpress using tiga theme with a bit o Discuss  |  Bury |  News | Learn C 8 4 Access functions and encapsulation [...]

  • five

    Alex-

    So if I’m understanding your venacular right, encapsulation is an upgrade thru conversion…I need to understand this stuff before I graduate to the next section…

    GeeeZZZZZuuuSSSSS! :)

  • DARK_BYTE

    Alex is it bad if I don’t declare the getters and setters by grouping them in two groups as u did above??
    For example I wrote this program:

    #include<iostream>
    #include<string>
    using namespace std;
    
    class Employees
    {
        string m_strId;
        string m_strName;
        int m_nAge;
    
    public:
        void SetId(string strId) { m_strId=strId; }
        string GetId() { return m_strId; }
    
        void SetName(string strName) { m_strName=strName; }
        string GetName() { return m_strName; }
    
        void SetAge(int nAge) { m_nAge=nAge; }
        int GetAge() { return m_nAge; }
    
    };
    
    int main()
    {
    
        Employees cEmployees;
        cEmployees.SetId("014897B");
        cEmployees.SetName("Whatever");
        cEmployees.SetAge(20);
        cout << cEmployees.GetId() << endl;
        cout << cEmployees.GetName() << endl;
        cout << cEmployees.GetAge() << endl;
        return 0;
    }
    
    • Quinn

      It’s not necessarily “bad”, per se, but there are several conditions where you wouldn’t want to make a getter and setter method for each class variable. Like Alex had mentioned before, a length variable for a String class should probably only have a getter interface, not a setter. So in the class you mentioned above, those are all probably perfectly reasonable interface methods (except perhaps SetId, which should, if set by the user at all, be set during instantiation by the constructor, covered in the next lesson). Also, you should have a convenience function that sets everything. I would build it like this:

      class Employee
      {
          string m_strId;
          string m_strName;
          int m_nAge;
      
      public:
          Employee(string empName, int empAge, string empId) {
              m_strId = empId;
              m_strName = empName;
              m_nAge = empAge;
          }
      
          string GetId() { return m_strId; }
      
          void SetName(string strName) { m_strName=strName; }
          string GetName() { return m_strName; }
      
          void SetAge(int nAge) { m_nAge=nAge; }
          int GetAge() { return m_nAge; }
      
          void SetEmpInfo(string strName, int nAge, string strId = "-1") {
              m_strName = strName;
              m_nAge = nAge;
              if (strId != "-1")
                  m_strId = strId;
          }
      };
      
      int main()
      {
      
          Employee cEmployee("Whatever", 20, "014897B");
          cout << cEmployee.GetId() << endl;
          cout << cEmployee.GetName() << endl;
          cout << cEmployee.GetAge() << endl << endl;
          cEmployee.SetEmpInfo("Brian", 26);
          cout << cEmployee.GetId() << endl;
          cout << cEmployee.GetName() << endl;
          cout << cEmployee.GetAge() << endl;
          return 0;
      }
  • Them damn gnomes!! They get everywhere!

  • Ngugi Thomas

    You are my best teachers

  • Marko

    Alex just want to say your website is better then most books there and i would dare to say that i will stick to this site now rather then even masterpieces like Stephen’s Prata Primer

    The way you explain things with examples and actually sticking to important things without writting novels is something that just works great with my mind.

  • yogal

    Wow, great explanation of encapsulation, and WHY one would use getters/setters instead of directly accessing members!

    One good argument for using encapsulation might be validation. When you have setters set-up you can validate what is passed to members and take appropriate action. Although members have to be of certain type, which provides low-level validation, sometimes you need a more complex requirement!

    Take care and thanks for such a wonderful tutorial!

  • Henry

    Hmm when i ran the last example – i got like -858993460. Any ideas?

  • Balu Nair

    Hi Alex,

    Your article rocks buddy! This article is such a precious gem.

    Thanks!!!

  • AsianBorat

    This tutorial is incredible. This article didn’t just explain encapsulation. It gave me a clear example, which FINALLY made me understand why encapsulation is so necessary.

  • yogendra

    great tutorial…..
    It gave me clear idea of what inheritance is and also
    the most basic difference between c and c++ is the
    code maintainability achieved through inheritance

  • DavidE

    Hey, I have a tip for those who like to consolidate their code. Instead of having both setValue() and getValue() methods, I do this:

    getset.h:

    #ifndef GETSET_H
    #define GETSET_H

    enum GetOrSet{
    GET,
    SET
    };

    #endif

    main.cpp

    #include <iostream>
    #include <cstdlib>
    #include "getset.h"

    class Employee{
    public:
    char name[25];
    int ID;
    double wage;
    void setInfo(char *name, int ID, double wage){
    strncpy(Employee::name, name, 25);
    Employee::ID = ID;
    Employee::wage = wage;
    }

    double Wage(GetOrSet option, double value=0){

    if(option == GET)
    return Employee::wage;
    else if (option == SET)
    Employee::wage = value;
    }

    void toString(){
    std::cout << "Name:\t" << name << std::endl << "ID:\t" << ID << std::endl << "Wage:\t$" << wage;
    }
    };

    inline void pause(){

    std::cout << std::endl;
    system("PAUSE");
    }

    int main(int argc, char* argv[]){

    Employee Alex;
    Alex.setInfo("Alex", 1, 38.00);
    Employee Jack;
    Jack.setInfo("Jack",2, 22.25);
    Jack.Wage(SET,50.0);
    Alex.toString();
    std::cout << std::endl;
    Jack.toString();
    std::cout << std::endl;
    std::cout << Alex.Wage(GET);

    pause();
    return 0;
    }

  • Ramseena M.S

    Now too I didn’t understand the real usage of declaring class members private

  • Tonyv

    Great site, though a little less impressed by some of the arguements for access functions. Like, “what happens if one changes the name of a member…” well, what if you change the name of the access function? The debug arguement is better, but a bit arbitrary. Why not use the same argument to put redundant functions all over the place? Best was the string/length example, though only relevent when one has this sort of situation.

    A final grump: c++ is an intrinsicly hard to read language because so often what something means depends on the context. An amusing exercise would be to list all the things “*p” could mean. So while I appreciate the efforts made to improve the “readability” of c++, I read this often enough to think the author(s) consider c++ readability a feature rather than a curse. There are certainly ways to write Chinese to make it more readable, but that does not mean it is a language designed to be easily read.

  • Michael Exavery

    I face a problem on declaring a class with array attributes.

You must be logged in to post a comment.