TweetFollow Us on Twitter

C++ Redux
Volume Number:10
Issue Number:1
Column Tag:Getting Started

Related Info: Color Quickdraw

C++ Redux

Rehashing the basics

By Dave Mark, MacTech Magazine Regular Contributing Author

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

Back in the October ‘93 Getting Started column, we took a look at the basics of object programming using C++. Since the column came out, I’ve gotten tons of feedback, from both C++ novices and experts alike. After sorting through all the comments, I took a few days and did a complete rewrite of the October column and of the corresponding chapter (Chapter 5) in Learn C++ on the Macintosh. If you’ve read the first printing of Learn C++ on the Macintosh (the changes will appear in the second printing) or made your way through the first edition of this column, please take the time to read this new version. As a bonus, this month’s column includes a C++ program that demonstrates the techniques described throughout the text (the October column didn’t include a program).

Objects

There is nothing mysterious about the concept of an object. In C++, an object is any instance of a data type. For example, this line of code:

intmyInt;

declares an int object. This column will teach you how to use C++ to create, destroy and manipulate objects in very powerful ways.

The first object we’ll take a look at is the structure.

The Organizational Power of the Struct

One of the most valuable features shared by C and C++ is the structure. Without the structure, you’d have no way to group data that belonged together. For example, suppose you wanted to implement an employee data base that tracked an employee’s name, employee ID, and salary. You might design a structure that looks like this:

const short kMaxNameSize = 20;

struct Employee
{
 char   name[ kMaxNameSize ];
 long   id;
 float  salary;
};

The great advantage of this structure is that it lets you bundle several pieces of information together under a single name. This concept is known as encapsulation.

For example, if you wrote a routine to print an employee’s data, you could write:

EmployeenewHire;
 •
 •
 •
PrintEmployee( newHire.name, newHire.id, newHire.salary );

Did you notice anything unusual about the declaration of newHire in the preceding code sample? In C, this code would not have compiled. Instead, the declaration would have looked like this:

struct Employee  newHire; /* The C version */

When the C++ compiler sees a structure declaration, it uses the structure name to create a new data type, making it available for future structure declarations.

On the other hand, it would be so much more convenient to pass the data in its encapsulated form:

PrintEmployee( &newHire );

Encapsulation allows you to represent complex information in a more natural, easily accessible form. In the C language, the struct is the most sophisticated encapsulation mechanism available. As you'll soon see, C++ takes encapsulation to a new level.

Encapsulating Data and Functions

While C structures are limited strictly to data, C++ supports structures composed of both data and functions.

Here's an example of a C++ structure declaration:

const short kMaxNameSize = 20;

struct Employee
{
// Data members...
 char   employeeName[ kMaxNameSize ];
 long   employeeID;
 float  employeeSalary;

// Member functions...
 void   PrintEmployee();
};

This example declares a new type named Employee. You can use the Employee type to declare individual Employee objects. Each Employee object is said to be a member of the Employee class.

The Employee class consists of three data fields as well as a function named PrintEmployee(). In C++, a classes’ data fields are known as data members and its functions are known as member functions.

Each Employee object you create gets its own copy of the Employee class data members. All Employee objects share a single set of Employee member functions.

Later in the column, you’ll see how to access an object’s data members and member functions. For now, let’s take a look at the mechanisms C++ provides to create and destroy objects.

Creating an Object

There are two ways to create a new object. The simplest method is to define the object directly, just as you would a local variable:

Employeeemployee1;

This definition creates an Employee object whose name is employee1. employee1 consists of a block of memory large enough to accomodate each of the three Employee data members.

When you create an object by defining it directly, as we did above, memory for the object is allocated when the definition moves into scope. That same memory is freed up when the object drops out of scope.

For example, you might define an object at the beginning of a function:

void  CreateEmployee()
{
 Employee employee1;

 •
 •
 •
}

When the function is called, memory for the object is allocated, right along with the function’s other local objects. When the function exits, the object’s memory is deallocated.

If you want a little more control over when your object is destroyed, take advantage of C++’s new operator.

First, define an object pointer, then use new to allocate the memory for your object. new returns a pointer to the newly created object. Here’s some code that creates an Employee object:

Employee*employeePtr;

employeePtr = new Employee;

The first line of code defines a pointer designed to point to an Employee object. The second line uses new to create an Employee object. new returns a pointer to the newly created Employee.

Accessing an Object’s Members

Once you’ve created an object, you can modify its data members and call its member functions. If you’ve defined the object directly, you’ll refer to its data members using the . operator:

Employeeemployee1;

employee1.employeeSalary = 200.0;

If you’re referencing the object through a pointer, use the -> operator:

Employee*employeePtr;

employeePtr = new Employee;

employeePtr->employeeSalary = 200.0;

To call a member function, use the same technique. If the object was defined directly, you’ll use the . operator:

Employeeemployee1;

employee1.PrintEmployee();

If you’re referencing the object through a pointer, you’ll use the -> operator:

Employee*employeePtr;

employeePtr = new Employee;

employeePtr->PrintEmployee();

The Current Object

In the previous examples, each reference to a data member or member function started with an object or object pointer. Inside a member function, however, the object or object pointer isn’t necessary to refer to the object for which the member function is executing.

For example, inside the PrintEmployee() function, you can refer to the data member employeeSalary directly, without referring to an object or object pointer:

if ( employeeSalary <= 200 )
 cout << "Give this person a raise!!!";

This code is kind of puzzling. What object does employeeSalary belong to? After all, you’re used to saying:

myObject->employeeSalary
instead of just plain:

employeeSalary

The key to this puzzle lies in knowing which object spawned the call of PrintEmployee() in the first place. Although this may not be obvious, a call to a non-static member function must originate with a single object.

Suppose you called PrintEmployee() from a non-Employee function (such as main()). You must precede this call with a reference to an object:

employeePtr->PrintEmployee();

Whenever a member function is called, C++ keeps track of the object used to call the function. This object is known as the current object.

In the call of PrintEmployee() above, the object pointed to by employeePtr is the current object. Whenever this call of PrintEmployee() refers to an Employee data member or function without using an object reference, the current object (in this case, the object pointed to by employeePtr) is assumed.

Suppose PrintEmployee() then called another Employee function. The object pointed to by employeePtr is still considered the current object. A reference to employeeSalary would still refer to the current object’s copy of employeeSalary.

The point to remember is, a non-static member function always starts up with a single object in mind.

The “This” Object Pointer

C++ provides a generic object pointer, available inside any member function, that points to the current object. The generic pointer has the name this. For example, inside every Employee function, the line:

this->employeeSalary = 400;
is equivalent to this line:

employeeSalary = 400;

this is useful when a member function wants to return a pointer to the current object, pass the address of the current object on to another function, or just store the address somewhere. This line of code:

return this;

returns the address of the current object.

Deleting an Object

When you create an object using new, you’ve got to take responsibility for destroying the object at the appropriate time. Just as a C programmer balances a call to malloc() with a call to free(), a C++ programmer balances each use of the new operator with an eventual use of the delete operator. Here’s the syntax:

Employee*employeePtr;

employeePtr = new Employee;

delete employeePtr;

As you’d expect, delete destroys the specified object, freeing up any memory allocated for the object. Note that this freed up memory only includes memory for the actual object and does not include any extra memory you may have allocated.

For example, suppose the object is a structure and one of its data members is a pointer to another structure. When you delete the first structure, the second structure is not deleted.

Writing Member Functions

Once your structure is declared, you’re ready to write your member functions. Member functions behave in much the same way as ordinary functions, with a few small differences. One difference, pointed out earlier, is that a member function has access to the data members and member functions of the object used to call it.

Another difference lies in the function implementation’s title line. Here’s a sample:

void  Employee::PrintEmployee()
{
 cout << "Employee Name:   " << employeeName << "\n";
}

Notice that the function name is preceded by the class name and two colons. This notation is mandatory and tells the compiler that this function is a member of the specified class.

The Constructor Function

Typically, when you create an object, you’ll want to perform some sort of initialization on the object. For instance, you might want to provide initial values for your object’s data members. The constructor function is C++’s built-in initialization mechanism.

The constructor function (or just plain constructor) is a member function that has the same name as the object’s class. For example, the constructor for the Employee class is named Employee(). When an object is created, the constructor for that class gets called.

Consider this code:

Employee*employeePtr;
employeePtr = new Employee;

In the second line, the new operator allocates a new Employee object, then immediately calls the object’s constructor. Once the constructor returns, the address of the new object is assigned to employeePtr.

This same scenario holds true in this declaration:

Employeeemployee1;

As soon as the object is created, its constructor is called.

Here’s our Employee struct declaration with the constructor declaration added in:

const short kMaxNameSize = 20;

struct Employee
{
// Data members...
 char   employeeName[ kMaxNameSize ];
 long   employeeID;
 float  employeeSalary;

// Member functions...
 Employee();
 void   PrintEmployee();
};

Notice that the constructor is declared without a return value. Constructors never return a value.

Here’s a sample constructor:

Employee::Employee()
{
 employeeSalary = 200.0;
}

This is proper form.

Adding Parameters to Your Constructor

If you like, you can add parameters to your constructor. Constructor parameters are typically used to provide initial values for the object’s data members. Here’s a new version of the Employee() constructor:

Employee::Employee( char *name, long id, float salary )
{
 strncpy( employeeName, name, kMaxNameSize );

 employeeName[ kMaxNameSize - 1 ] = '\0';

 employeeID = id;
 employeeSalary = salary;
}

The constructor copies the three parameter values into the corresponding data members.

The object that was just created is always the constructor’s current object. In other words, when the constructor refers to an Employee data member, such as employeeName or employeeSalary, it is referring to the copy of that data member in the newly created object.

This line of code supplies the new operator with a set of parameters to pass on to the constructor:

employeePtr = new Employee( "Dave Mark", 1000, 200.0 );

This line of code does the same thing without using new:

Employeeemployee1( "Dave Mark", 1000, 200.0 );

As you’d expect, this code creates an object named employee1 by calling the Employee constructor, passing it the three specified parameters.

Just for completeness, here’s the class declaration again, showing the new constructor:

struct Employee
{
// Data members...
 char   employeeName[ kMaxNameSize ];
 long   employeeID;
 float  employeeSalary;

// Member functions...
 Employee( char *name, long id, float salary );
 void   PrintEmployee();
};

The Destructor Function

The destructor function is called for you, just as the constructor is. Unlike the constructor, however, the destructor is called when an object in its class is deleted or goes out of scope. Use the destructor to clean up after your object before it goes away. For instance, you might use the destructor to deallocate any additional memory your object may have allocated.

The destructor function is named by a tilda character (~) followed by the class name. The destructor for the Employee class is named ~Employee(). The destructor has no return value and no parameters.

Here’s a sample destructor:

Employee::~Employee( void )
{
 cout << "Deleting employee #" << employeeID << "\n";
}

If you created your object using new, the destructor is called when you use delete:

Employee*employeePtr;

employeePtr = new Employee;

delete employeePtr;

If your object was defined directly, the destructor is called just before the object is destroyed. For example, if the object was declared at the beginning of a function, the destructor is called when the function exits.

Here’s an updated Employee class declaration showing the constructor and destructor:

struct Employee
{
// Data members...
 char   employeeName[ kMaxNameSize ];
 long   employeeID;
 float  employeeSalary;

// Member functions...
 Employee( char *name, long id, float salary );
 ~Employee();
 void   PrintEmployee();
};

Access Priveleges

When you declare a class, you need to decide which data members and functions you’d like to make available to the rest of your program. C++ gives you the power to hide a classes’ functions and data from all the other functions in your program, or allow access to a select few.

For example, consider the Employee class we’ve been working with throughout the column. In the current model, an Employee’s name is stored in a single array of chars. Suppose you wrote some code that created a new Employee, specifying the name, id, and salary, then later in your program you decided to modify the Employee’s name, perhaps adding a middle name provided while your program was running.

With the current design, you could access and modify the Employee’s employeeName data member from anywhere in your program. As time passes and your program becomes more complex, you might find yourself accessing employeeName from several places in your code.

Now imagine what happens when you decide to change the implementation of employeeName. For example, you might decide to break the single employeeName into three separate data members, one each for the first, middle and last names. Imagine the hassle of having to pore through your code finding and modifying every single reference to employeeName, making sure you adhere to the brand new model.

C++ allows you to hide the implementation details of a class (the specific type of each data member, for example), funneling all access to the implementation through a specific set of interface routines. By hiding the implementation details, the rest of your program is forced to go through the interface routines your class provides. That way, when you change the implementation, all you have to do is make whatever changes are necessary to the classes interface, without having to modify the rest of your program.

The mechanism C++ provides to control access to your classes’ implementation is called the access specifier.

Access Specifiers

C++ allows you to assign an access specifier to any of a classes’ data members and member functions. The access specifier defines which of your program’s functions have access to the specified data member or function. The access specifier must be one of public, private, or protected.

If a data member or function is marked as private, access to it is limited to member functions of the same class (or, as you’ll see later in the chapter, to classes or member functions marked as a friend of the class).

On the flip side, the public specifier gives complete access to the member function or data member, limited only by scope.

By default, the data members and member functions of a class declared using the struct keyword are all public. By adding the private keyword to our class declaration, we can limit access to the Employee data members, forcing the outside world to go through the provided member functions:

struct Employee
{
// Data members...
 private:
 char   employeeName[ kMaxNameSize ];
 long   employeeID;
 float  employeeSalary;

// Member functions...
 public:
 Employee( char *name, long id, float salary );
 ~Employee();
 void   PrintEmployee();
};

Once the compiler encounters an access specifier, all data members and functions that follow are marked with that code, at least until another code is encountered. In this example, the three data members are marked as private and the three member functions are marked as public.

The class Keyword

So far, all of our classes have been created using the struct keyword. You can also create classes, using the exact same syntax, substituting the keyword class for struct. The only difference is, the members of a struct are all public by default and the members of a class are all private by default.

Why use class instead of struct? If you start with a struct, you give the world complete access to your class members unless you intentionally limit access using the appropriate access specifiers. If you start with a class, access to your class members is limited right from the start. You have to intentionally allow access by using the appropriate access specifiers.

For the remainder of this book, we’ll use the class keyword to declare our classes. Here’s the new version of the Employee class:

class Employee
{
// Data members...
 private:
 char   employeeName[ kMaxNameSize ];
 long   employeeID;
 float  employeeSalary;

// Member functions...
 public:
 Employee( char *name, long id, float salary );
 ~Employee();
 void   PrintEmployee();
};

Notice that the private access specifier is still in place. Since the members of a class-based class are private by default, the private access specifier is not needed here, but it does make the code a little easier to read. The public access specifier is necessary, however, to give the rest of the program access to the Employee member functions.

With all that we’ve covered so far, we’re about ready for our next sample program. Employee.cp brings these concepts together.

An Object Programming Example

Create a new folder named Employee in your development folder. Then, launch Symantec C++ and create a new project, named Employee.Π, in the Employee folder. Next, select Add Files... from the Project menu and navigate into the Symantec C++ for Macintosh folder and then into the Standard Libraries folder. You’ll be adding three libraries to this project. Add the ANSI++, CPlusLib, and IOStreams libraries to the project. When the libraries appear in the project window, drag CPlusLib and IOStreams to a new segment (just click on them, one at a time, and drag them towards the bottom of the project window. The Project Manager will create the new segment for you automatically).

Next, create a new source code file and save it as Employee.cp inside the Employee folder. Add the file to the project. If it doesn’t get added to the same segment as CPlusLib and IOStreams, drag it into that segment. ANSI++ should be in one segment and the three other files should be in a different segment.

Here’s the source code for Employee.cp:

/* 1 */
#include <iostream.h>
#include <string.h>

const short kMaxNameSize = 20;

class Employee
{
// Data members...
 private:
 char   employeeName[ kMaxNameSize ];
 long   employeeID;
 float  employeeSalary;

// Member functions...
 public:
 Employee( char *name, long id, float salary );
 ~Employee();
 void   PrintEmployee();
};

Employee::Employee( char *name, long id, float salary )
{
 strncpy( employeeName, name, kMaxNameSize );

 employeeName[ kMaxNameSize - 1 ] = '\0';

 employeeID = id;
 employeeSalary = salary;
 
 cout << "Creating employee #" << employeeID << "\n";
}

Employee::~Employee()
{
 cout << "Destroying employee #" << employeeID << "\n";
}

void  Employee::PrintEmployee()
{
 cout << "-----\n";
 cout << "Name:   " << employeeName << "\n";
 cout << "ID:     " << employeeID << "\n";
 cout << "Salary: " << employeeSalary << "\n";
 cout << "-----\n";
}

intmain()
{
 Employee employee1( "Dave Mark", 1, 200.0 );
 Employee *employee2;

 employee2 = new Employee( "Steve Baker", 2, 300.0 );

 employee1.PrintEmployee();
 employee2->PrintEmployee();

 delete employee2;
 
 return 0;
}

Save your source code, and select Run from the Project menu. Symantec C++ will compile and run your program. Here’s what the output should look like:

/* 2 */
Creating employee #1
Creating employee #2
-----
Name:   Dave Mark
ID:     1
Salary: 200
-----
-----
Name:   Steve Baker
ID:     2
Salary: 300
-----
Destroying employee #2
Destroying employee #1

Let’s take a look at the source code.

The employee Source Code

As you look through employee.cp, you should see some familiar sights. This program takes the Employee class described throughout this column through its paces.

The first thing you’ll notice is the two include files <iostream.h> which is like the C++ version of <stdio.h> (we’ll talk about the iostream library in a later column) and <string.h>, which is needed for the call to strncpy() later in the program:

#include <iostream.h>
#include <string.h>

The const kMaxNameSize and the Employee class declaration are identical to those presented earlier in the column. Notice that the data members are all marked as private (unnecessary, but it does make the code easier to read) while the member functions are marked as public.

const short kMaxNameSize = 20;

class Employee
{
// Data members...
 private:
 char   employeeName[ kMaxNameSize ];
 long   employeeID;
 float  employeeSalary;

// Member functions...
 public:
 Employee( char *name, long id, float salary );
 ~Employee();
 void   PrintEmployee();
};

The Employee class has three member functions: a constructor, a destructor, and a utility routine named PrintEmployee(). The constructor, Employee(), uses its three parameters to initialize each of the Employee data members.

Employee::Employee( char *name, long id, float salary )
{

To avoid a possible non-terminated string in the name parameter, we’ll use strncpy() to copy all the bytes from name into employeeName. strncpy() copies kMaxNameSize characters from name to employeeName. If the name string is less than kMaxNameSize characters long, strncpy() will also copy over the null-terminator.

 strncpy( employeeName, name, kMaxNameSize );

If name is not null-terminated or is kMaxNameSize bytes long or longer, we’ll stick a null-terminator at the very end of employeeName to ensure that one exists.

 employeeName[ kMaxNameSize - 1 ] = '\0';

Finally, we’ll copy the remaining two parameters into their respective data members.

 employeeID = id;
 employeeSalary = salary;

Once the data members are initialized, the constructor sends a message to the console, telling us which Employee object was just created.

 cout << "Creating employee #" << employeeID << "\n";
}

Since no extra memory was allocated, there’s not a whole lot for the destructor to do. Just like the constructor, the destructor sends a message to the console, telling us which Employee object will be deleted.

Employee::~Employee()
{
 cout << "Deleting employee #" << employeeID << "\n";
}

PrintEmployee() displays the contents of the three data members of the current object:

void  Employee::PrintEmployee()
{
 cout << "-----\n";
 cout << "Name:   " << employeeName << "\n";
 cout << "ID:     " << employeeID << "\n";
 cout << "Salary: " << employeeSalary << "\n";
 cout << "-----\n";
}

main() is the control center, where all the action is. First, we define an Employee object, passing three parameters to the constructor:

intmain()
{
 Employee employee1( "Dave Mark", 1, 200.0 );

As the Employee constructor is called, it displays the following line on the console:

Creating employee #1

Next, an Employee object pointer is defined:

 Employee *employee2;

This time, new is used to create a second Employee object:

 employee2 = new Employee( "Steve Baker", 2, 300.0 );

Once again, the Employee constructor is called, sending another line to the console:

Creating employee #2

Now, both objects are used to call the PrintEmployee() member function. employee1 is an object and uses the . operator to access its member function. Since employee2 is a pointer and uses the -> operator to access the PrintEmployee() function:

 employee1.PrintEmployee();
 employee2->PrintEmployee();

These two calls result in the following output:

-----

Name: Dave Mark

ID: 1

Salary: 200

-----

-----

Name: Steve Baker

ID: 2

Salary: 300

-----

Next, the object pointed to by employee2 is deleted:

 delete employee2;
}

This causes employee2’s destructor to be called, resulting in this line of output:

Destroying employee #2

Finally, main() exits and all of main()’s local variables (including employee1) are deallocated. As soon as employee1 was deallocated, its destructor was called, resulting in a final line of output being sent to the console:

Destroying employee #1

Notice that employee1’s destructor wasn’t called till main() had exited.

Take another look at your program’s output. If you like, go run the program again. Notice that every single line of output was produced by an object’s member function. Although you did call PrintEmployee() directly, the constructor and destructor functions were called for you when you created and deleted an object.

Consider the line of code used to delete an Employee object:

delete employee1;

This line of code does not contain a function call. It does not contain code that prints information to the console. Even so, a function call was made (the destructor function, called for you). A line of output was sent to the console.

The point here is that there’s action going on behind the scenes. Stuff happens automatically. You delete an object, the destructor gets called for you. This might seem like a minor point, but this is your first peek at the power of object programming.

Till Next Month

Interested in more C++ coverage? Let me know. You can write to me c/o MacTech magazine at the addresses listed on page 2 of the magazine (Under the heading How to communicate with Xplain Corporation). In the meantime, I’ll go back to the Mac Toolbox and more Color QuickDraw in next month’s column. See you then...

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Ableton Live 11.3.11 - Record music usin...
Ableton Live lets you create and record music on your Mac. Use digital instruments, pre-recorded sounds, and sampled loops to arrange, produce, and perform your music like never before. Ableton Live... Read more
Affinity Photo 2.2.0 - Digital editing f...
Affinity Photo - redefines the boundaries for professional photo editing software for the Mac. With a meticulous focus on workflow it offers sophisticated tools for enhancing, editing and retouching... Read more
SpamSieve 3.0 - Robust spam filter for m...
SpamSieve is a robust spam filter for major email clients that uses powerful Bayesian spam filtering. SpamSieve understands what your spam looks like in order to block it all, but also learns what... Read more
WhatsApp 2.2338.12 - Desktop client for...
WhatsApp is the desktop client for WhatsApp Messenger, a cross-platform mobile messaging app which allows you to exchange messages without having to pay for SMS. WhatsApp Messenger is available for... Read more
Fantastical 3.8.2 - Create calendar even...
Fantastical is the Mac calendar you'll actually enjoy using. Creating an event with Fantastical is quick, easy, and fun: Open Fantastical with a single click or keystroke Type in your event details... Read more
iShowU Instant 1.4.14 - Full-featured sc...
iShowU Instant gives you real-time screen recording like you've never seen before! It is the fastest, most feature-filled real-time screen capture tool from shinywhitebox yet. All of the features you... Read more
Geekbench 6.2.0 - Measure processor and...
Geekbench provides a comprehensive set of benchmarks engineered to quickly and accurately measure processor and memory performance. Designed to make benchmarks easy to run and easy to understand,... Read more
Quicken 7.2.3 - Complete personal financ...
Quicken makes managing your money easier than ever. Whether paying bills, upgrading from Windows, enjoying more reliable downloads, or getting expert product help, Quicken's new and improved features... Read more
EtreCheckPro 6.8.2 - For troubleshooting...
EtreCheck is an app that displays the important details of your system configuration and allow you to copy that information to the Clipboard. It is meant to be used with Apple Support Communities to... Read more
iMazing 2.17.7 - Complete iOS device man...
iMazing is the world’s favourite iOS device manager for Mac and PC. Millions of users every year leverage its powerful capabilities to make the most of their personal or business iPhone and iPad.... Read more

Latest Forum Discussions

See All

Motorsport legends NASCAR announce an up...
NASCAR often gets a bad reputation outside of America, but there is a certain charm to it with its close side-by-side action and its focus on pure speed, but it never managed to really massively break out internationally. Now, there's a chance... | Read more »
Skullgirls Mobile Version 6.0 Update Rel...
I’ve been covering Marie’s upcoming release from Hidden Variable in Skullgirls Mobile (Free) for a while now across the announcement, gameplay | Read more »
Amanita Design Is Hosting a 20th Anniver...
Amanita Design is celebrating its 20th anniversary (wow I’m old!) with a massive discount across its catalogue on iOS, Android, and Steam for two weeks. The announcement mentions up to 85% off on the games, and it looks like the mobile games that... | Read more »
SwitchArcade Round-Up: ‘Operation Wolf R...
Hello gentle readers, and welcome to the SwitchArcade Round-Up for September 21st, 2023. I got back from the Tokyo Game Show at 8 PM, got to the office here at 9:30 PM, and it is presently 11:30 PM. I’ve done what I can today, and I hope you enjoy... | Read more »
Massive “Dark Rebirth” Update Launches f...
It’s been a couple of months since we last checked in on Diablo Immortal and in that time the game has been doing what it’s been doing since its release in June of last year: Bringing out new seasons with new content and features. | Read more »
‘Samba De Amigo Party-To-Go’ Apple Arcad...
SEGA recently released Samba de Amigo: Party-To-Go () on Apple Arcade and Samba de Amigo: Party Central on Nintendo Switch worldwide as the first new entries in the series in ages. | Read more »
The “Clan of the Eagle” DLC Now Availabl...
Following the last paid DLC and free updates for the game, Playdigious just released a new DLC pack for Northgard ($5.99) on mobile. Today’s new DLC is the “Clan of the Eagle" pack that is available on both iOS and Android for $2.99. | Read more »
Let fly the birds of war as a new Clan d...
Name the most Norse bird you can think of, then give it a twist because Playdigious is introducing not the Raven clan, mostly because they already exist, but the Clan of the Eagle in Northgard’s latest DLC. If you find gathering resources a... | Read more »
Out Now: ‘Ghost Detective’, ‘Thunder Ray...
Each and every day new mobile games are hitting the App Store, and so each week we put together a big old list of all the best new releases of the past seven days. Back in the day the App Store would showcase the same games for a week, and then... | Read more »
Urban Open-World RPG ‘Project Mugen’ Fro...
Last month, NetEase Games revealed a new free to play open world RPG tentatively titled Project Mugen for mobile, PC, and consoles. I’ve liked the setting and aesthetic since its first trailer, and today’s new video has the Game Designer and... | Read more »

Price Scanner via MacPrices.net

Apple AirPods 2 with USB-C now in stock and o...
Amazon has Apple’s 2023 AirPods Pro with USB-C now in stock and on sale for $199.99 including free shipping. Their price is $50 off MSRP, and it’s currently the lowest price available for new AirPods... Read more
New low prices: Apple’s 15″ M2 MacBook Airs w...
Amazon has 15″ MacBook Airs with M2 CPUs and 512GB of storage in stock and on sale for $1249 shipped. That’s $250 off Apple’s MSRP, and it’s the lowest price available for these M2-powered MacBook... Read more
New low price: Clearance 16″ Apple MacBook Pr...
B&H Photo has clearance 16″ M1 Max MacBook Pros, 10-core CPU/32-core GPU/1TB SSD/Space Gray or Silver, in stock today for $2399 including free 1-2 day delivery to most US addresses. Their price... Read more
Switch to Red Pocket Mobile and get a new iPh...
Red Pocket Mobile has new Apple iPhone 15 and 15 Pro models on sale for $300 off MSRP when you switch and open up a new line of service. Red Pocket Mobile is a nationwide service using all the major... Read more
Apple continues to offer a $350 discount on 2...
Apple has Studio Display models available in their Certified Refurbished store for up to $350 off MSRP. Each display comes with Apple’s one-year warranty, with new glass and a case, and ships free.... Read more
Apple’s 16-inch MacBook Pros with M2 Pro CPUs...
Amazon is offering a $250 discount on new Apple 16-inch M2 Pro MacBook Pros for a limited time. Their prices are currently the lowest available for these models from any Apple retailer: – 16″ MacBook... Read more
Closeout Sale: Apple Watch Ultra with Green A...
Adorama haș the Apple Watch Ultra with a Green Alpine Loop on clearance sale for $699 including free shipping. Their price is $100 off original MSRP, and it’s the lowest price we’ve seen for an Apple... Read more
Use this promo code at Verizon to take $150 o...
Verizon is offering a $150 discount on cellular-capable Apple Watch Series 9 and Ultra 2 models for a limited time. Use code WATCH150 at checkout to take advantage of this offer. The fine print: “Up... Read more
New low price: Apple’s 10th generation iPads...
B&H Photo has the 10th generation 64GB WiFi iPad (Blue and Silver colors) in stock and on sale for $379 for a limited time. B&H’s price is $70 off Apple’s MSRP, and it’s the lowest price... Read more
14″ M1 Pro MacBook Pros still available at Ap...
Apple continues to stock Certified Refurbished standard-configuration 14″ MacBook Pros with M1 Pro CPUs for as much as $570 off original MSRP, with models available starting at $1539. Each model... Read more

Jobs Board

Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
Operations Associate - *Apple* Blossom Mall...
Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Retail Key Holder- *Apple* Blossom Mall - Ba...
Retail Key Holder- APPLE BLOSSOM MALL Brand: Bath & Body Works Location: Winchester, VA, US Location Type: On-site Job ID: 03YM1 Job Area: Store: Sales and Support Read more
Omnichannel Associate - *Apple* Blossom Mal...
Omnichannel Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.