Search

8.3 — Public vs private access specifiers

Public and private members

Consider the following struct:

In this program, we declare a DateStruct and then we directly access its members in order to initialize them. This works because all members of a struct are public members by default. Public members are members of a struct or class that can be accessed from outside of the struct or class. In this case, function main() is outside of the struct, but it can directly access members month, day, and year, because they are public.

On the other hand, consider the following almost-identical class:

If you were to compile this program, you would receive errors. This is because by default, all members of a class are private. Private members are members of a class that can only be accessed by other members of the class. Because main() is not a member of DateClass, it does not have access to date’s private members.

Access specifiers

Although class members are private by default, we can make them public by using the public keyword:

Because DateClass’s members are now public, they can be accessed directly by main().

The public keyword, along with the following colon, is called an access specifier. Access specifiers determine who has access to the members that follow the specifier. Each of the members “acquires” the access level of the previous access specifier (or, if none is provided, the default access specifier).

C++ provides 3 different access specifier keywords: public, private, and protected. Public and private are used to make the members that follow them public members or private members respectively. The third access specifier, protected, works much like private does. We will discuss the difference between the private and protected access specifier when we cover inheritance.

Mixing access specifiers

Classes can (and almost always do) use multiple access specifiers to set the access levels of each of its members. There is no limit to the number of access specifiers you can use in a class.

In general, member variables are usually made private, and member functions are usually made public. We’ll take a closer look at why in the next lesson.

Rule: Make member variables private, and member functions public, unless you have a good reason not to.

Let’s take a look at an example of a class that uses both private and public access:

This program prints:

10/14/2020

Note that although we can’t access date’s members variables m_month, m_day, and m_year directly from main (because they are private), we are able to access them indirectly through public member functions setDate() and print()!

The group of public members of a class are often referred to as a public interface. Because only public members can be accessed from outside of the class, the public interface defines how programs using the class will interface with the class. Note that main() is restricted to setting the date and printing the date. The class protects the member variables from being accessed or edited directly.

Some programmers prefer to list private members first, because the public members typically use the private ones, so it makes sense to define the private ones first. However, a good counterargument is that users of the class don’t care about the private members, so the public ones should come first. Either way is fine.

Access controls work on a per-class basis

Consider the following program:

One nuance of C++ that is often missed or misunderstood is that access control works on a per-class basis, not a per-object basis. This means that when a function has access to the private members of a class, it can access the private members of any object of that class type that it can see.

In the above example, copyFrom() is a member of DateClass, which gives it access to the private members of DateClass. This means copyFrom() can not only directly access the private members of the implicit object it is operating on (copy), it also means it has direct access to the private members of DateClass parameter d! If parameter d were some other type, this would not be the case.

This can be particularly useful when we need to copy members from one object of a class to another object of the same class. We’ll also see this topic show up again when we talk about overloading operator<< to print members of a class in the next chapter.

Structs vs classes revisited

Now that we’ve talked about access specifiers, we can talk about the actual differences between a class and a struct in C++. A class defaults its members to private. A struct defaults its members to public.

That’s it!

(Okay, to be pedantic, there’s one more minor difference -- structs inherit from other classes publicly and classes inherit privately. We’ll cover what this means in a future chapter, but this particular point is practically irrelevant since you should never rely on the defaults anyway).

Quiz time

1a) What is a public member?

Show Solution

1b) What is a private member?

Show Solution

1c) What is an access specifier?

Show Solution

1d) How many access specifiers are there, and what are they?

Show Solution

2a) Write a simple class named Point3d. The class should contain:
* Three private member variables of type double named m_x, m_y, and m_z;
* A public member function named setValues() that allows you to set values for m_x, m_y, and m_z.
* A public member function named print() that prints the Point in the following format: <m_x, m_y, m_z>

Make sure the following program executes correctly:

This should print:

<1, 2, 3>

Show Solution

2b) Add a function named isEqual() to your Point3d class. The following code should run correctly:

Show Solution

3) Now let’s try something a little more complex. Let’s write a class that implements a simple stack from scratch. Review lesson 7.9 -- The stack and the heap if you need a refresher on a what a stack is.

The class should be named Stack, and should contain:
* A private fixed array of integers of length 10.
* A private integer to keep track of the length of the stack.
* A public member function named reset() that sets the length to 0 and all of the element values to 0.
* A public member function named push() that pushes a value on the stack. push() should return false if the array is already full, and true otherwise.
* A public member function named pop() that pops a value off the stack and returns it. If there are no values on the stack, it should assert out.
* A public member function named print() that prints all the values in the stack.

Make sure the following program executes correctly:

This should print:

( )
( 5 3 8 )
( 5 3 )
( )

Show Solution

8.4 -- Access functions and encapsulation
Index
8.2 -- Classes and class members

124 comments to 8.3 — Public vs private access specifiers

  • DecSco

    Hey there,

    when I've initialised my variable m_next to zero, why not use the following function for reset()?

    I thought, whether or not there are garbage values at further indices is of no concern, if the access to these is impossible.

    EDIT: Got it. I thought my version is better for performance, but actually, you wouldn't ever need to reset the array anyway, just move the "pointer".

  • Levi

    Hey alex question 3 was a good one.
    Altough you might want to add a semicolin on line 7 of the awnser: static const int s_maxStackLength{ 10 }

    PS,
    awesome tutorials they are helping me alot!

  • Gabi

    One thing I think it would be nice to be mentioned is that you can use public/private in a single class how many times you want.

  • Peter Baum

    What is the difference (advantages/disadvantages) between using

    and

    • nascardriver

      Hi Peter!

      There are <c*> headers for a lot of <*.h> headers.
      The <c*> headers are the C++ version. They declare their contents in the @std namespace whereas the <*.h> versions declare their contents globally.
      Most <c*> versions make their contents available globally, this however is not standard behavior and should not be relied upon.
      Use the <c*> header to avoid naming collisions.

      Also note that there is not such thing as @std::assert. @assert is a macro and cannot be namespaced. I don't think there's anything else in the assert header. I'd still go with <cassert> for consistency.

  • Peter Baum

    regarding the soluton to 3: As an alternative to putting 10 sprinkled in the code or calculating it as @J Gahr did, we could write

    We can also use a "for each" and make the assert() more readable.  
    Then the whole thing would look like:

    • Alex

      Good calls. For-each could definitely be used instead of regular for when initializing. Do note that your for-each element needs to be a reference if you want to change the value of the item being iterated though. Otherwise you're changing a copy.

  • Peter Baum

    Regarding given solution for 2a:

    Why the "private" rather than using the default?  After all, we were told about it and given examples of it.  If we shouldn't use the default, then why is there a default at all?  If the default is being used for protection, the compiler could alternatively treat no specification as an error.

    • nascardriver

      Hi Peter!

      One reason I can think of is that it's easier to get along when switching between multiple languages. For example Scala uses public as the default modifier. By explicitly stating the access modifier there's no need to think about what the language uses as default.

    • Alex

      Two reasons I can think of:
      1) It's better to be explicit about your intent than implicit.
      2) If you later add something with a different access specifier above (e.g. a public function), then your formerly private-by-default members would use that access specifier, which may not be what you'd intended.

  • Cumhur

    when reseting, we assign 0 value to each array element. So why printer does not print 0's?

    • nascardriver

      Hi Cumhur!

      @Stack::reset sets @Stack::m_next to 0.
      @Stack::print prints the elements from 0 to @Stack::m_next. Since @Stack::m_next is 0, there are no elements to be printed.

      • Peter Baum

        Or maybe we should be asking why bother to initialize the stack at all and if for some reason we did, why use 0 rather than something more distinctive?

        • nascardriver

          > why bother to initialize the stack at all
          Other than for debugging purposes there is no reason to do so. Not resetting the stack is faster.

          > why use 0 rather than something more distinctive?
          What would that be? 0 is the most distinctive number I can think of.

  • J Gahr

    Concerning quiz question 3, are there any performance benefits/hindrances from having my class member functions be void (e.g. for the pop() function):

    Instead of return by value:

    Also, is it recommended not to initialize variables within a struct or class? My full solution to #3 is below for context.

    • nascardriver

      Hi J!

      > are there any performance benefits
      Yes, your code is faster.

      > is it recommended not to initialize variables within a struct or class?
      Initialize your variables. Either directly or in every constructor. The automatically generated default constructor should zero-initialize all members, I still like to do it on my own, just to be sure.

      • J Gahr

        I really appreciate the informative feedback, thanks nascardriver! I was unaware that passing by value can sometimes be faster in certain situations.

    • Peter Baum

      Regarding the void pop() - Although not tested or used in the quiz, frequently we would want to use the value popped off the stack, and thus the return value would be a convenient way to obtain this value.

  • Hi, iJust wanted to ask what are member variables and functionns? could you please xplain them to me through an example ?

    • nascardriver

      Hi Ali!

      Everything that's declared inside a class is a member of that class.

  • Matt

    Every time I forget how much optimization can be done with clever use of pre and post increment/decrement I think I'll remember next time...then I don't.

    I'll remember next time...

    Edit: Question about the website: Why do my code lines not match the line counters on the left?  On a 1080p monitor at 100% zoom (using Chrome) this post shows line 84 next to "Sample Output" and the rest of the lines below that have no line counter, for example.  By line 16 it is off by almost exactly one line, getting linearly worse the further down the page you look.

    Edit2: After posting that edit and reloading the page, it seems to have realigned itself.

    • nascardriver

      Hi Matt!

      > how much optimization can be done with clever use of pre and post increment/decrement
      It doesn't give you any advantage at all, because your compiler will use the prefix operator whenever it can. It's just one of those "If your compiler didn't do optimization, this would potentially save you one instruction". Still, use the prefix whenever you can.

      General: Use this-> to access members. It makes your code easier to understand and prevents naming conflicts. It's not covered until lesson 8.8, but you might as well start now so you don't get into the habit of omitting it. Example with your @push function:

      Line 11: You don't need that 0. It was required in C. In C++ it only changes the value of the first element. Arrays with empty uniform initializers are zero-initialized.

      Line 39: This is only required when you're planning on printing all 10 elements of the stack since you'll never be able to access that value again.

      Line 57: I'd go with an empty uniform initialization. It doesn't have any effect in this case. But if you didn't initialize all other values so well this would save you.

  • Travis Touchdown

    Alex,

    Thank you very much for your lessons.

    Would like to ask why do we need "const" for the access controls?

    • nascardriver

      Hi Travis!

      The 'const' isn't required here, however, when dealing with references, you should always use const parameters if you don't need to modify the parameter.

  • Vili Sinervä

    Hi! I was wondering if the resetting of the stack would work like this:

    Wouldn't this be slightly faster as the for-loop iterates over fewer items(not that it matters in such a simple and fast piece of code)?

    • nascardriver

      Hi Vili!

      You're right, that's a good idea.

      • Alex

        Well, the instructions do say, "A public member function named reset() that sets the length to 0 and all of the element values to 0." This implementation doesn't set _all_ of the element values to 0 (only the ones that were used).

        Technically you could optimize this further by just setting m_next to 0, and not setting any element values to 0. It would function the same, it would just show up in the debugger differently because you'd have garbage values in your unused elements.

  • Frederico

    I have a question concerning structs vs classes.

    In the previous lesson you said that it’s fair to assume a class will clean up after itself (e.g. a class that allocates memory will deallocate it before being destroyed), but it’s not safe to assume a struct will.

    But in this lesson you said that the only difference is the default access specifiers.

    Will a class clean up after itself automatically?

    • nascardriver

      Hi Frederico!

      > Will a class clean up after itself automatically?
      No it won't. Whenever you write a class that allocates memory on it's own you should also handle the deallocation of that memory. That's why it's fair to assume a class will clean up after itself.
      Structs are usually only used to store data and don't do anything with it. When memory is allocated in a struct then it has been allocated by the user (coder) and the struct won't deallocate it on it's own.

  • Max

    Hi there!

    For quiz 3, in my reset() function I originally tried to do this with a for-each loop.

    However, I got thrown a "write access violation" exception. Made my program crash right away. It works perfectly with a regular for loop with essentially the same things. I was wondering what happened and more importantly why it didn't work.

    • nascardriver

      Hi Max!

      A for-each loop doesn't iterate over indexes, it iterates over elements!

      Output

      Your program is crashing, because you're trying to access an invalid index in @m_array.

      • Max

        Ah of course, how could I have forgotten that... I edited my code and changed the loop accordingly, and it works now with the for-each. Thanks for the refresher!

  • akde

    Hi Alex,

    Your website is more than awesome. Thanks a lot.

    Can you have a look at my code. The problem is that the array (fA) changes but when I try to print(using myStack.print) it I see it remained unchanged.

    // sandBox.cpp : Defines the entry point for the console application.
    //

    #include    "stdafx.h"
    #include    <iostream>
    #include    <string>
    #include    <array>

    class Stack
    {
    public:
        void reset(Stack stackInp)
        {
            //    Set all values to 0.
            fA.fill(0);
        }

        void print()
        {
            std::cout << "\n";
            
            std::cout << "fA is \n";
            for (int ctr = 0; ctr < 10; ctr++)
            {
                std::cout << fA[ctr] << "\t";
            }
            std::cout << "\n";
        }

        bool push(int inp)
        {
            if (los==10)
            {
                std::cout << "\n stack is full!\n";
                return false;
            }
            else
            {
                //    Dummy array is needed to copy.
                std::array<int, 10> dum{};

                //    Shifting is implemented via copy.
                for (int ctr = 0; ctr < 9; ctr++)
                {
                    dum[ctr + 1] = fA[ctr];
                }
                std::cout << std::endl;

                //    Copy the element (that is pushed) into the first location.
                dum[0] = inp;

                //    copy dum into fA.
                auto fA = dum;

                //    Display fA after push.
                std::cout << "\n After push fA is : ";
                for (int iii = 0; iii < 10; iii++)
                {
                    std::cout << fA[iii];
                }
                //    Increase number of elemets in the stack by 1.
                los++;
                std::cout << "\nNow there are " << los << "Element(s) in the stack\n";
                return true;
            }
        }

    private:
        //    Initialize all values with 0.
        std::array<int, 10> fA{};
        //    At first, there are no elements in the stack.
        int los{ 0 };
    };

    int main()
    {
        Stack    myStack;

        myStack.print();
        myStack.push(2);
        myStack.print();
        return 0;
    }

    • Alex

      The problem here is this line:

      Because you've provided a type for fA, the compiler will define a new variable named fA that will shadow the member variable of the same name. Remove the "auto" and it should work.

      Two other thoughts:
      * You don't need the temporary dum array -- if you copy elements from back to front, you can shift all the elements down one using the same array.
      * Rather than pushing new variables on the front of the array (cause you to have to shift everything down), why don't you push them on the end, where you can just change the size of the array?

      • akde

        Thanks a lot Alex.
        I am pushing the new variables to the top because the stack is Last In First Out type of structure (http://www.learncpp.com/cpp-tutorial/79-the-stack-and-the-heap/). But apparently your suggestion makes much more sense here (bcs it makes it a lot easier).

        This web site and your immediate responses are doing great job btw.

  • Samawia Khan

    What is main use of private class and public class????

  • Kushagra

    This code is not working properly

    #include <iostream>
    class Stack
    {
        int array[10];
        int length;
        
        public:
        void reset()
        {
            length = 0;
            for (int i =0; i< 10; i++)
            {
                array[i] = 0;
            }
        }
        bool push(int x)
        {
                if (length>=10)
            {
                    std::cout << " stack is full";
            return false;
            }
            array[length] = x;
            
            
            
            length++;
            
        
            return true;
        }
        int  pop()
        {
            if (length<0)
            std::cout << " stack is empty ";
            else
            {
                array[length] = 0;
            length--;
            std::cout<<" " << std::endl;
            return array[length-1];
            }
        }
        
            void print()
            {
                if (length >=10)
                std::cout<< " stack is full " <<std::endl;
                else
                {
                
                for (int i = 0; i<(length); i++)
                {
                    std::cout<< array[i]<<" " ;
                }
                std::cout<< std::endl;
                }
            
            
            }
    };
    int main()
    {
        Stack stack;
        stack.reset();

        stack.print();

        stack.push(5);
        stack.push(3);
        stack.push(8);
        
        stack.print();
        stack.push(8);
        
        stack.print();
        stack.push(8);
        
        stack.print();
        stack.push(8);
        
        stack.print();
        stack.push(8);
        
        stack.print();
        stack.push(8);
        
        stack.print();
        stack.push(8);
        stack.print();
        stack.push(8);
        
        stack.print();
        stack.push(8);
        
        stack.print();
        stack.pop();
        stack.print();
        stack.pop();
        stack.print();
        stack.pop();

        stack.print();
        
        return 0;
    }

  • RP

    Hey how memory is allocated to an object when you initiate it?

    • Alex

      It depends. Objects can be allocated on the stack (for local variables), on the heap (dynamically allocated), or in the code or data segments of the executable (for global variables).

  • AMG

    Alex,
    In my opinion, for only one reason "private" should precedes "public", because it's easier (more intuitive) to understand functions, defined under "public", and, most likely, use private variables. Similar flow is in function definition.

  • Brian M

    Alex,

        I think your tutorials are great but I tend to have some problems with your  quizzes(or the way they are worded) a lot of the times. For an example  on quiz 3) it says to make a private int "to keep track of the length of the stack", yet in the solution you use m_next "to hold the index of the next free element on the stack".

    Also could you explain how this is representing the stack?

    The way I see it is main() is on the bottom of the stack, main() calls(pushes) stack.reset(), then returns(pops) back to main(). Repeat this for the rest of the push/pop/print functions. So for the final function stack.print() the stack would look like this:

    stack.print()
    main()

    Since it is in the middle of the print() function when it prints it would still be on the the stack. To me I don't understand what the result is suppose to be here. All I see is 5, 3, 8 which is just the numbers that we assigned to different elements of the array. Maybe i'm just looking at this completely wrong.

    Thank you.
    Brian

    • Alex

      It sounds like you're confusing the call stack (which is used to handle function calls) with the concept of a stack data structure (which can hold a bunch of arbitrary values). This quiz deals with the latter.

      Consider a set of 10 mailboxes, numbered 0 through 9. You can use these like a stack by always sticking mail in the lowest numbered free mailbox, and only taking mail out of the highest numbered full mailbox.

      That's what the m_next index is doing -- keeping track of which mailbox is the next free one, so we don't have to iterate over all the mailboxes to figure it out each time. m_next is the length of the current stack.

      I'm not sure how helpful that was. Let me know what I can clarify.

      • Brian M

        Alex

        This was actually very helpful, thank you! You are correct, I was not aware of the concept of the stack data structure, Looking back now I notice I misread lesson 7.9. I remember reading the mailbox analogy but I assumed there was only one stack(the call stack). I see now you also explained a stack data structure. I just thought they were the same thing. Thank you for clearing this up!

        Brian

  • JimD

    Hi Alex,

    I'm not getting why the function definition parameter in quiz question 2d takes the 'const' type:

            

    What does that signify, and will that ever take a different type?

    Ace tutorials by the way. 🙂

    Cheers,
    Jim

    • JimD

      Please ignore that question - I've got it now. It was a bad idea to skip a few pages to get to the interesting bits. 🙁

    • Alex

      It signifies the parameter is a reference to a const value -- meaning we're not allowed to change the object being referenced through p. It also helps the caller know that isEqual() won't change anything.

      It's not strictly necessary here, since we don't do anything that necessitates the const, but it's good practice.

  • Nicolas

    the first time we do reset() and then print() why doesn't it print 10 zeroes? every element in m_array[10] is 0 so 'std::cout << m_array[i]' should print a zero, right?

  • :|

    The other lessons arent working, is there a problem with the website or is from my side?

  • Hamed O.Khaled

    Hey Alex!! I've read all questions and the answers.
    there is only 1 thing I confused about what is the difference between compile time and run time or in other words what is the actual reason that make the lines below give error..thanks and sorry for the dumping question.

    • Alex

      Compile time = when compiling your program. Runtime = when running your program.
      Please see http://www.learncpp.com/cpp-tutorial/61-arrays-part-i/ and http://www.learncpp.com/cpp-tutorial/2-9-symbolic-constants-and-the-const-keyword/ for more information on both of these topics.

  • SUNNY

    One more difference between class and object is in during inheritance.
    Struct inherit publicly by default and classes privately.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]