Search

12.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

A class can (and almost always does) 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 interact 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

Question #1


a) What is a public member?

Show Solution

b) What is a private member?

Show Solution

c) What is an access specifier?

Show Solution

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

Show Solution

Question #2


a) Write a simple class named Point3d. The class should contain:
* Three private member variables of type int 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

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

Show Solution

Question #3


Now let’s try something a little more complex. Let’s write a class that implements a simple stack from scratch. Review lesson 11.8 -- The stack and the heap if you need a refresher on 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 size of the stack.
* A public member function named reset() that sets the size 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, the code should exit via an assert.
* 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


12.4 -- Access functions and encapsulation
Index
12.2 -- Classes and class members

338 comments to 12.3 — Public vs private access specifiers

  • Lesommeil

    I wonder about case and default. They feel like implemented different ways about access specifier.

  • Waldo Lemmer

    - Question #2b)'s solution, line 23: `Point3d &p`
    - Question #3's solution has 2 consecutive "private:" sections

  • Malma

    Why we use

    Instead of

    • Alex

      It ensures that m_x gets initialized to a 0 value if somehow code is added later that forgets to initialize m_x. It's a good defensive programming tactic.

  • Christofle

    My answer for question 3 :)

  • Al Curtis

    I thought a stack meant that you always take from stack[0], so that all the elements are shifted to the right when pushed, and shifted left when popped.  This took me a lot longer than it perhaps should have, still, it works and I learned something!

  • SuperNoob

    Is this code alright?

  • KR

    For question #3, I used "void" instead of "int" for pop() and was able to output the desired results.  Am I missing something as to why we should being returning an int when it doesn't seemed to be used? Or is this just completely wrong and I got lucky with the output?

    • nascardriver

      If `pop()` returns `void`, there's no way to access the elements of the stack. You either need a `top()` function that returns the element on top or have `pop()` return the popped element.

  • mr. mister

    on question #3, in the function pop(), i think you meant return m_array[m_next--];
    doesnt yours return the element before the top element?

  • kio

    Hi Alex and Nascardriver,

    I've made mistake, you had me on "fix" array, so I thought I should create C style array :/

  • kio

    Hi Alex and Nascardriver,

    Should the following part of

    be like this? Because it's getter?

    • nascardriver

      Returning by `const` value does more bad than good (It prevents return-value optimization and moving). The `isEqual()` function should be `const`. We show how to do that later in this chapter.

  • Grey

    Damn! Quiz 3 totally jammed my brain. I couldn't analyze the "pop" method for almost a week now. I found a lot of easy ways to do so, should I keep trying with your method or move along?

    • nascardriver

      There's nothing in `pop()` you shouldn't already know.

      1. At the start `m_next` is 0
      2. You push an element, then `m_next` is 1
      3. When you call `pop()`, `m_next` needs to be decreased and you need to return `m_array[0]`

      4. --operator returns the new value of the variable, so we can move it into the subscript operator

      Can you pinpoint what you don't understand?

      • jorge van vestenberg

        this is what i still don't get:

        we reach and execute return statement, and then what's gonna happen?
        From what i see here, i don't see something that can pop the pointed value out of the array.
        Can you please clarify to me? Thanks before

        • nascardriver

          The value doesn't have to be removed from the array (or set to 0).
          The only way to access the array element at the popped of index would be to call `push()` first, overriding whatever value was left behind. Setting the popped-off element to 0 in `pop()` wouldn't have a visible effect.

          • jorge van vestenberg

            i'm sorry, i still get none from here,
            how is the process of popping off again?
            are you saying that by simply accessing the value of the targetted element, the value is gonna be gone / popped off automatically?
            this is just the only thing that makes me confused really hard. I mean, how? because i just see a return statement (and assert statement for safety reason) there and this doesn't make any sense to me. Just how does the popping off process happen? that's it

            Thanks again, please pardon my slow brain here.

            • nascardriver

              The element is never removed from the array, it's only being made inaccessible. For the user of `Stack`, is appears as if the element has been removed.

              • jorge van vestenberg

                aaaaahhh....... i see it now!!

                damn it! :D so that's why you ask to create private integer member in the class,
                it's actually to control the access of the STACK member, right?

                sorry, i was just focusing on the ARRAY in the class member bit too much, hence the misunderstanding.. hehe

                Thanks so much.

  • J34NP3T3R

    for QUIZ #3

    ok i totally failed this quiz. i created functions that literally push_back pop_back and clear the stack.

  • J34NP3T3R

    could not seem to initialize a size for a vector inside a class but works inside main().

    • nascardriver

      You can't use direct-initialization for non-static member initialization.
      Without using a constructor, I don't think you can avoid repeating the type

      If you find a non-redundant solution without using a constructor, please let me know.

  • J34NP3T3R

    QUESTIONS for QUIZ #1 :

    1.) Do we still have to explicitly declare the variables as Public and Private even if they already are by default ?
        or is this a standard we need to follow for a safer approach ?

    2.) am i mistaken or there seem to be a risk when declaring variables as PRIVATE and PUBLIC

    • nascardriver

      1) The first `private` is optional. I like using it to be explicit.

      2) `setValues()` is private. It's always risky to forget something.

  • First try with Question 3! :)

  • Maxim

    Can anyone clarify what's going on in this code???
    I really don't understand! i set array to be 10 in length but i still can access the 11th value.
    And what is the way to assign values separetly?

    • mao zedung

      If I had to take a guess, I'd say that m_b is placed right after m_a in memory by compiler. Operator[] does no bounds checking, so it will happily get you the 11th item, meaning the next integer-sized chunk after the array, and since array items and m_b are same types, it even interprets it correctly.

      • Maxim

        well.. changing m_b from int to double works quite expectedly

  • yeokaiwei

  • yeokaiwei

    This is what I did for Quiz 3.

    I hope this is acceptable.

    • nascardriver

      - Use `false`/`true` rather than `0`/`1` for booleans.
      - Line 25: Magic number, should be `fixedarray.size()`
      - Line 25-27: You don't need an if-statement for this. See lesson 4.10 Quiz #2
      - Line 37 has no observable effect. This element is inaccessible after being popped. If you want inaccessible elements to be 0 for debugging purposes, `reset` should also set all elements to 0.

      Good solution otherwise. I'm getting a spooky vibe that you're not using an auto-formatter. VS can format your source code for you.

      • yeokaiwei

        I changed Line 25-27 to

        Some confusion though, I will get an error with the following.
        Yet, I can print it out with std::cout.
        Why is this so?

        • nascardriver

          `sizeofstack` is an `int`, but `fixedarray.size()` is some unsigned integer type.
          Comparison of signed and unsigned integers can cause unexpected results, which is why you're getting a warnings without the cast.

          • yeokaiwei

            Oh, int and uint are 2 different types so they can't be compared?

            Thanks.

            • nascardriver

              They can be compared, but the signed type will be converted to unsigned first

              • yeokaiwei

                Oh, because i{-4} wraps around to 4,294,967,295-4.

                This means you are comparing 4,294,967,291 to 9.

                Either way, even if it's huge, it's still a uint.

                It's just a big value.

                Why can't it compare?

Leave a Comment

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