OOP need some help with conversion from noob 0.0 to noob 0.1b level

Hi All,
My first post here :slight_smile:

Requirements;
4 Outputs, separate variable on/off times, counter to serial monitor/LCD, pause on input high to a pin

A fairly basic sketch and I have semi-working code, but I'd like to learn and make it better...

So my existing code (trimmed to 1 output for sanitys sake)

const int Out1 = 9;
const long intervalOut1 = 1000;
long CountOut1 = 0;

int ledStateOut1 = HIGH;
//int ledStateOut2 = LOW;
//int ledStateOut3 = HIGH;
//int ledStateOut4 = LOW;

unsigned long previousMillisOut1 = 0;

void setup() {
  Serial.begin(9600);
  pinMode(Out1, OUTPUT);
}

void loop() {
  Serial.print("Out 1, ");
  Serial.print(CountOut1);
  unsigned long currentMillisOut1 = millis();

  // Out1 //
  if (currentMillisOut1 - previousMillisOut1 >= intervalOut1)
  {
    previousMillisOut1 = currentMillisOut1;
    if (ledStateOut1 == LOW)
    {
      ledStateOut1 = HIGH;
      CountOut1 ++;
    }
    else
    {
      ledStateOut1 = LOW;
    }
    digitalWrite(Out1, ledStateOut1);
  }
}

and i have that 4 times in a row replacing the "1" with 2,3,4.
I wanted to add different on/off times, and I knew there had to be a better way rather than repeating the same chunks of code over and over....
so i went digging around and found lady Ada's example of OOP - All together now! | Multi-tasking the Arduino - Part 1 | Adafruit Learning System
and adapted it to a point for my own use

class PulseOutput
{
    int outPin;
    long OnTime;
    long OffTime;

    
    int outState;
    unsigned long previousMillis;


public:
PulseOutput(int pin, long on, long off)
    {
      outPin = pin;
      pinMode(outPin, OUTPUT);

      OnTime = on;
      OffTime = off;

      outState = LOW;
      previousMillis = 0;
    }


    void Update()
    {
     
      unsigned long currentMillis = millis();

      if ((outState == HIGH) && (currentMillis - previousMillis >= OnTime))
      {
        outState = LOW;
        previousMillis = currentMillis;
        digitalWrite(outPin, outState);
      }
      else if ((outState == LOW) && (currentMillis - previousMillis >= OffTime))
      {
        outState = HIGH;  // turn it on
        previousMillis = currentMillis;
        digitalWrite(outPin, outState);
      }
    }
};

// pin, on, off
PulseOutput led1(9, 100, 100);
PulseOutput led2(10, 100, 900);
PulseOutput led3(11, 1900, 100);
PulseOutput led4(13, 63, 63);

void setup()
{
  Serial.begin(9600);

}

void loop()
{
   if (digitalRead(5) == HIGH)
  {
  led1.Update();
  }
  if (digitalRead(6) == HIGH)
  {
    led2.Update();
  }
  if (digitalRead(7) == HIGH)
  {
    led3.Update();
  }
  if (digitalRead(8) == HIGH)
  {
    led4.Update();
  }
}

I havent got as far as the serial monitor output on this, but for the life of my I can NOT get the counter to integrate into the 'update' part of the code

I'd post what I've tried, but frankly it is embarrassing.
Any hints, clues, or just a push in the right direction on what I need to add would be good.

thanks :slight_smile:

huxmut:
but for the life of my I can NOT get the counter to integrate into the 'update' part of the code

What does that mean? I don't see any attempt to put a counter in your "OOP" code. You should probably have a counter as one of your private member variables.

Couple other points:

  • You seem to know that the millis() function returns values of type 'unsigned long':
unsigned long currentMillis = millis();

So why did you do this:

long OnTime;
long OffTime;

Do you think those values will ever be negative? If not, use an 'unsigned long':

  • Or this:
int outPin;

Your board doesn't have negative pin number and certainly it has no more than 255. So, use a 'byte'.

Same thing here:

int outState;

You only use HIGH and LOW. But your 'int' variable can hold a number between -32768 and +32767. A 'byte' will do nicely here also.

So, get a counter into your class and update() function, clean up your data types, and post the results. If they're still not what you want, folks here will help you improve it.

in the class, declare an int (or long) counter, but put it under the 'public' keyword

This makes the variable accessible outside of the class. Anything declared above the public statement is private to the class, by default.

You can increment the counter in the same way as your 'normal' code

to access the counter, use the '.' operator eg:

Serial.print(led1.counter)

Perform a search for "C++ access a class variable" and look at some of the Stack Exchange results (some will be confusing, or not applicable. However, they are useful to know) Also visit the cplusplus.com results. They're enlightening as well.

darrob:
in the class, declare an int (or long) counter, but put it under the 'public' keyword

I respectfully disagree. The OOP tenets of abstraction and encapsulation strongly suggest that a variable member like 'counter' should be private to the class and that the class be solely responsible for its manipulation. To provide for influence from outside the class, it should provide public member functions (methods) that manipulation the variable in a controlled manner.

gfvalvo:
I respectfully disagree. The OOP tenets of abstraction and encapsulation strongly suggest that a variable member like 'counter' should be private to the class and that the class be solely responsible for its manipulation. To provide for influence from outside the class, it should provide public member functions (methods) that manipulation the variable in a controlled manner.

As I'm not really a C++ (OOP) programmer, I just found an easy way of accessing a class variable.

Adding a method to print the value of the counter is easy enough. However, does that mean that you have to update your class to cater for all possible usages of that variable?

For example if your code requires that you do something (not relevant to the class itself) once the counter has reached a certain limit?. How would you know when that limit has been reached? Do you then just not worry about including a counter in your class and put in additional code in the main loop to count the number of times that particular LED is turned on? Or do you make the variable accessible if required?

The tenets are valid (don't get me wrong). I just wonder if there is scope for being able to access some stuff outside of the class if required.
[EDIT]
This is genuine curiosity here. Not defending my own pre-conceptions

I think the point here is that if you are going to ignore (no dis-respect) the inherent security of a class, why include it to begin with? You could simply declare it a global and move on. If you do install it in a class, code etiquette would suggest that you adhere to the intent.

DKWatson:
I think the point here is that if you are going to ignore (no dis-respect) the inherent security of a class, why include it to begin with? You could simply declare it a global and move on. If you do install it in a class, code etiquette would suggest that you adhere to the intent.

Declaring it (counter, in this case) as a global would mean that additional code would be required to check if the LED has been turned on, then increment if that is the case. It makes more sense for the class to do that at the same time as it is turning the LED on.

So essentially you're (both) saying that if it is to be included in a class, additional methods need to added if interaction with it in any way is required. Doing anything else is (while doable) not good practice.

Huh! Okay! I think that that's sinking into my brain.
K&R are still shouting in my hindbrain, though. I'm still getting used to all of this new fangled C++ stuff. :wink:

I wanted to add different on/off times, and I knew there had to be a better way rather than repeating the same chunks of code over and over....

No need to use a class to achieve that objective, simply use arrays

const byte ledPins[] = {3,5,6,9};
const byte NUMBER_OF_LEDS = sizeof(ledPins);
unsigned long startTimes[NUMBER_OF_LEDS] = {};
byte indexes[NUMBER_OF_LEDS] = {0};
const byte MAX_NUMBER_OF_PERIODS = 10;
unsigned long periods[][MAX_NUMBER_OF_PERIODS] =    //periods for each LED.  zero indicates end of sequence
{
  {1000, 2000, 1500, 2500, 0},
  {500, 200, 1000, 2000, 3000, 4000, 0},
  {400, 1000, 1500, 2000, 0},
  {1100, 2200, 0}
};

void setup()
{
  Serial.begin(115200);
  for (int led = 0; led < NUMBER_OF_LEDS; led++)
  {
    pinMode(ledPins[led], OUTPUT);
  }
}

void loop()
{
  unsigned long currentTime = millis();
  for (int led = 0; led < NUMBER_OF_LEDS; led++)  //iterate through the LEDs
  {
    if (currentTime - startTimes[led] >= periods[led][indexes[led]])  //? time to change ?
    {
      digitalWrite(ledPins[led], !digitalRead(ledPins[led]));  //flip led state
      startTimes[led] = currentTime;  //save start time of state
      indexes[led]++;                    //increment index
    }
    if (periods[led][indexes[led]] == 0)  //if next period is zero (end of sequence)
    {
      indexes[led] = 0;        //reset period index for this LED
    }
  }  //end for loop
}

Having said that, when I wanted to teach myself the basics of OOP I created this library PinToggle library

Please feel free to us/abuse it as you wish

gfvalvo:
What does that mean? I don't see any attempt to put a counter in your "OOP" code. You should probably have a counter as one of your private member variables.

Couple other points:
(snip)

thanks for the reply. i've cleaned up my variables. thanks for the tips on those.
i didn't put my counter attempts in as my lack of understanding is embarrassing
saying that, here it is in all its glory :s

// conversion from chunk to object oriented class attempt 1 billion
// Current - 4 Outputs of variable on/off times, pause on input high/low
// Future - counter, serial monitor, lcd

class PulseOutput
{
    byte outPin;
    unsigned long OnTime;
    unsigned long OffTime;
    long OnCounter;
    byte outState;
    unsigned long previousMillis;

  public:
    PulseOutput(int pin, long on, long off, long OnCounter)
    {
      outPin = pin;
      pinMode(outPin, OUTPUT);
      OnTime = on;
      OffTime = off;
      outState = LOW;
      previousMillis = 0;
    }

    void Update()
    {
      unsigned long currentMillis = millis();
      if ((outState == HIGH) && (currentMillis - previousMillis >= OnTime))
      {
        outState = LOW;
        previousMillis = currentMillis;
        digitalWrite(outPin, outState);
      }
      else if ((outState == LOW) && (currentMillis - previousMillis >= OffTime))
      {
        outState = HIGH;  // turn it on
        previousMillis = currentMillis;
        digitalWrite(outPin, outState);
        OnCounter ++;
      }
    }
};

// pin, on, off
PulseOutput led1(9, 1000, 1000);
PulseOutput led2(10, 500, 1500);
PulseOutput led3(11, 1900, 100);
PulseOutput led4(13, 1000, 1000);

void setup()
{
  Serial.begin(9600);
}

void loop()
{
    Serial.print("Out 1, ");
    Serial.print(led1.OnCounter);
  /*
    Serial.print("; Out 2, ");
    Serial.print(led2.counter);
    Serial.print("; Out 3, ");
    Serial.print(led3.counter);
    Serial.print("; Out 4, ");
    Serial.println(led4.counter);
  */

  if (digitalRead(8) == HIGH)
  {
    led1.Update();
    led2.Update();
    led3.Update();
  }
  led4.Update();

}

I think i'm closer now, the current compile error is

Pulse_Gen_v06:51: error: no matching function for call to 'PulseOutput::PulseOutput(int, int, int)'

 PulseOutput led1(9, 1000, 1000);

                               ^

C:\Users\***\Documents\Arduino\Pulse_Gen_v06\Pulse_Gen_v06.ino:51:31: note: candidates are:

C:\Users\***\Documents\Arduino\Pulse_Gen_v06\Pulse_Gen_v06.ino:21:5: note: PulseOutput::PulseOutput(int, long int, long int, long int)

     PulseOutput(int pin, long on, long off, long OnCounter)

.......and continues on repeatedly ......

darrob:
in the class, declare an int (or long) counter, but put it under the 'public' keyword

This makes the variable accessible outside of the class. Anything declared above the public statement is private to the class, by default.

You can increment the counter in the same way as your 'normal' code

to access the counter, use the '.' operator eg:

Serial.print(led1.counter)

Perform a search for "C++ access a class variable" and look at some of the Stack Exchange results (some will be confusing, or not applicable. However, they are useful to know) Also visit the cplusplus.com results. They're enlightening as well.

Thanks for the hints. i'm very much not at 0.1b it seems, maybe alpha ? :slight_smile:

UKHeliBob:
No need to use a class to achieve that objective, simply use arrays

(snip)

Having said that, when I wanted to teach myself the basics of OOP I created this library PinToggle library
(snip)

much appreciate the effort, but my (fairly old and abused) brain can only focus on one thing at a time.
I'll certainly look into arrays at some point in time. maybe that would have been easier to go with, but i have invested much brain strain on this OOP and I'd like to see it working now :slight_smile:

I'll have a look at that library soon too
thanks :slight_smile:

darrob:
K&R are still shouting in my hindbrain, though. I'm still getting used to all of this new fangled C++ stuff. :wink:

One basic premise in C++ is to prevent, or at least minimize, the risk of self-inflicted damage. This is provided to a degree by abstraction and encapsulation. There are always ways around it, but if you follow coding guidelines that demand strict adherence to the rules, the work-arounds are no-nos. By having a variable in a class declared as private, you ensure that there will be no 'accidental' modifications. Global variables are notorious for being modified when you least desire. Especially when there are those who choose names that are easily confused and can point to the wrong data with a simple typo.

A little story. Back in the 70's I was at a job where a box of cards (029 keypunch days) was dumped on my desk and I was told to sort it out. If you have any familiarity of what a line printer was/is and the 7x9 dot matrix output from them, as well as the print line across the top of the card itself, you know it's not very clear. My predecessor, who had announced he was quitting the day he left, had coded every variable to be 8 characters long using combinations of O (uppercase o) and 0 (zero). You want to talk about typos getting in the way of debugging?

Rules are in place, generally, for a good reason. Ignoring them may be okay for you, but have some sympathy for whoever else has to go through it. And yes, the protective measures sometimes mean heavier code.

i have invested much brain strain on this OOP and I'd like to see it working now :slight_smile:

I know exactly how you feel. It is very satisfying to get things working. Once I had got the basic library working I could not stop adding functionality, just because I could, some of which is undocumented. I found the whole process very satisfying.

I got some good feedback from the forum (see pinToggle library - comments please - Programming Questions - Arduino Forum) although exposing your work to others can be a scary process, but I survived.

DKWatson:
If you have any familiarity of what a line printer was/is

[Shudder]Dealing with the crumpled mass of what was supposed to be the overnight print run when the paper jammed :o , or the infernal noise when I had to wander into the computer room. Stripping the perforated bits off of the sides of reams of paper then tearing the fanfold into individual pages? Oh thanks for bringing that back to me :slight_smile:

Sweet memories. Our computer was over 500 miles away and used in production. We could submit one coding change a day and if we were lucky it was run that night and we'd have results the next day. Talk about productivity.

I never got to do programming on a professional level.
I was more operations and support. ie the poor sod person who got called to deal with a keyboard full of coffee, or the one on hands and knees trying to find which particular idiot staff member kicked the co-axial out of the wall while everyone else resorted to sneakernet while the network was down. ::slight_smile:
Good times :smiley:

**edit - it is working, I'm an idiot.... But still some questions as below, and I've put the new code below too

So I have "public" counter working ...
I'm really confused on the public not public bits... if someone can look at my code and tell me how to do it 'correctly' it would be much appreciated :slight_smile:

// 4 Outputs, variable on/off times, pause on input low
// Future - counter, serial mon, bluetooth, lcd

class PulseOutput
{
    byte outPin;
    unsigned long OnTime;
    unsigned long OffTime;
    byte outState;
    unsigned long previousMillis;

  public:
    unsigned long Count;
    PulseOutput(int pin, long on, long off, unsigned long Count)
    {
      outPin = pin;
      pinMode(outPin, OUTPUT);
      OnTime = on;
      OffTime = off;
      outState = LOW;
      previousMillis = 0;
    }

    void Update()
    {
      unsigned long currentMillis = millis();
      if ((outState == HIGH) && (currentMillis - previousMillis >= OnTime))
      {
        outState = LOW;
        previousMillis = currentMillis;
        digitalWrite(outPin, outState);
      }
      else if ((outState == LOW) && (currentMillis - previousMillis >= OffTime))
      {
        outState = HIGH;  // turn it on
        previousMillis = currentMillis;
        digitalWrite(outPin, outState);
        Count ++;
      }
    }
};

// pin, on, off, ?Count?
PulseOutput led1(9, 100, 400, 1);
PulseOutput led2(10, 500, 1500, 1);
PulseOutput led3(11, 190, 1000, 1);
PulseOutput led4(13, 63, 63, 1);

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  Serial.print(" A ");
  Serial.print(led1.Count);
  Serial.print("  || B ");
  Serial.print(led2.Count);
  Serial.print("  || C ");
  Serial.print(led3.Count);
  Serial.print("  || D ");
  Serial.println(led4.Count);

  if (digitalRead(2) == HIGH)
  {
    led1.Update();
    led2.Update();
    led3.Update();
  }
  led4.Update();
}

Is not your declaration of unsigned long Count; as public and then again in your constructor, creating two instances? Maybe,

PulseOutput(int pin, long on, long off, unsigned long use_some_other_name_here)
{
    outPin = pin;
    pinMode(outPin, OUTPUT);
    OnTime = on;
    OffTime = off;
    outState = LOW;
    previousMillis = 0;
}
unsigned long Count = use_some_other_name_here;

Right you are. Thanks.
I still dont really understand whats going on in that code .... some heavy reading required...

but i have this now ticking away, and it appears it's actually correct or close ??
I changed the times to be something more brain friendly to work out if things are correct

So times of

// pin, on, off, ?Count?
PulseOutput led1(9, 100, 900, 1);
PulseOutput led2(10, 100, 1900, 1);
PulseOutput led3(11, 100, 2900, 1);
PulseOutput led4(13, 100, 4900, 1);

gives an output that looks like

 A 498  || B 253  || C 169  || D 102
 A 498  || B 253  || C 169  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 498  || B 253  || C 170  || D 102
 A 499  || B 253  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 499  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 102
 A 500  || B 254  || C 170  || D 103
 A 500  || B 254  || C 170  || D 103
 A 500  || B 254  || C 170  || D 103
 A 500  || B 254  || C 170  || D 103
 A 500  || B 254  || C 170  || D 103
 A 500  || B 254  || C 170  || D 103
 A 500  || B 254  || C 170  || D 103
 A 500  || B 254  || C 170  || D 103
 A 501  || B 254  || C 170  || D 103
 A 501  || B 255  || C 171  || D 103
 A 501  || B 255  || C 171  || D 103
public:
    unsigned long Count;
    PulseOutput(int pin, long on, long off, unsigned long mCount)
    {
      outPin = pin;
      pinMode(outPin, OUTPUT);
      OnTime = on;
      OffTime = off;
      outState = LOW;
      previousMillis = 0;
    }

    void Update()
    {
      unsigned long currentMillis = millis();
      if ((outState == HIGH) && (currentMillis - previousMillis >= OnTime))
      {
        outState = LOW;
        previousMillis = currentMillis;
        digitalWrite(outPin, outState);
      }
      else if ((outState == LOW) && (currentMillis - previousMillis >= OffTime))
      {
        outState = HIGH;  // turn it on
        previousMillis = currentMillis;
        digitalWrite(outPin, outState);
        Count ++;
      }
    }

There you go. You really ought to get in the mode of understanding rather than playing pin-the-tail.

One trick you will see in a lot of C++ is to use "_" in front of private variables. Then your constructor which initializes Count would look more like...

    unsigned long _Count;
    PulseOutput(int pin, long on, long off, unsigned long Count)
    {
      outPin = pin;
      pinMode(outPin, OUTPUT);
      OnTime = on;
      OffTime = off;
      outState = LOW;
      previousMillis = 0;
      _Count = Count;
    }

Why does Count have a capital C anyway? The usual pattern you will see in Arduino code is to always make the first letter lowercase and then intermediate words use camelCase. (See, the humps in a camel, gettit?)

Capitalised names are usually the name of a class, such as Serial or your example PulseOutput.

OK, so why do you make certain things private? So that external parts of the program can't mess with them. Additionally, external parts of the program don't become dependent on internal details inside the class. Look at a regular Arduino library which uses a .h file to declare the parts of the library callable from your program. Everything inside the .cpp file might get upgraded by a new version of the library but your program still works because it wasn't dependent on the names of the internal details in the class implementation.

I'm having some regrets in making my initial suggestion in the first place.
While it does make life very easy, it does appear to lead to bad programming habits

It is better practice to add a method to return the value of Count, rather than directly accessing it. It does lead to a bit more code, but actually not that much.

I've reworked the latest version you posted to include such a method.

class PulseOutput
{
  byte outPin;
  unsigned long OnTime;
  unsigned long OffTime;
  byte outState;
  unsigned long previousMillis;
  unsigned long Count;  // ******* Note that count has been moved from the public section
  
  public:
    
    PulseOutput(int pin, long on, long off, unsigned long mCount)
    {
      outPin = pin;
      pinMode(outPin, OUTPUT);
      OnTime = on;
      OffTime = off;
      outState = LOW;
      previousMillis = 0;
    }
    
    void Update()
    {
      unsigned long currentMillis = millis();
      if ((outState == HIGH) && (currentMillis - previousMillis >= OnTime))
      {
        outState = LOW;
        previousMillis = currentMillis;
        digitalWrite(outPin, outState);
      }
      else if ((outState == LOW) && (currentMillis - previousMillis >= OffTime))
      {
        outState = HIGH;  // turn it on
        previousMillis = currentMillis;
        digitalWrite(outPin, outState);
        Count ++;
      }
    }
    
    // ***** A new method to return the value of count
    unsigned long getCount()
    {
      return Count;
    }
};

The way it gets used is very similar to accessing the variable directly.

...
void loop()
{
  Serial.print(" A ");
  Serial.print(led1.getCount());
  Serial.print("  || B ");
  Serial.print(led2.getCount());
  Serial.print("  || C ");
  Serial.print(led3.getCount());
  Serial.print("  || D ");
  Serial.println(led4.getCount());

  if (digitalRead(2) == HIGH)
  {
    led1.Update();
    led2.Update();
    led3.Update();
  }
  led4.Update();
}

If you need to update the value of Count later on, another method can be added. For example

[code[
class PulseOutput
{
  ...
  public:
  ...
  void setCount(unsigned long newCountVal)
  {
    Count = newCountVal;
  }
};

changing the value of count in your loop() would then be
[code]
void loop()
{
  ...
  led1.setCount(SOME_NEW_VALUE);
  ...
}

The value of such an approach, I have since discovered, is this

Multiple Sources:
A lot of research into good software engineering and programmer productivity indicates that it's typically good to hide the details of how something is implemented.
If person A writes a class, then s/he has certain assumptions about how the class should work.
If person B wants to use the class, then s/he often has different assumptions about how the class should work (especially if person A did not document the code well, or even at all, as is the case all too often).
Then person B is likely to misuse the data in the class, which can break how the class methods work, and lead to errors that are difficult to debug, at least for person B.
In addition, by hiding the details of the class implementation, person A has the freedom to completely rework the implementation, perhaps removing the variable and replacing it with something else. This can occur because person A figures out a better way to implement the variable, or because that variable was in there only as a debugging tool and is not necessary to the actual working of the class, etc.
Programmers don't write code only for themselves. In general, they write code for other people, that will be maintained for other people. "Other people" includes you five years from now, when you look at how you implemented something and ask yourself, What on earth was I thinking? By keeping the details of the implementation hidden (including data) you have the freedom to change that, and client classes/software don't need to worry about it as long as the interface remains the same.

So, I've shown you that is is easy to access Count directly. But getting into the habit of doing so via methods now is better.