Suppose that you want to assemble your own PC, you go to a hardware store and pick up a motherboard, a processor, some RAMs, a hard disk, a casing, a power supply, and put them together. You turn on the power, and the PC runs. You need not worry whether the motherboard is a 4-layer or 6-layer board, whether the hard disk has 4 or 6 plates; 3 inches or 5 inches in diameter, whether the RAM is made in Japan or Korea, and so on. You simply put the hardware components together and expect the machine to run. Of course, you have to make sure that you have the correct interfaces, i.e., you pick an IDE hard disk rather than a SCSI hard disk, if your motherboard supports only IDE; you have to select RAMs with the correct speed rating, and so on. Nevertheless, it is not difficult to set up a machine from hardware components.
Similarly, a car is assembled from parts and components, such as chassis, doors, engine, wheels, brake, and transmission. The components are reusable, e.g., a wheel can be used in many cars [of the same specifications].
Hardware, such as computers and cars, are assembled from parts, which are reusable components.
How about software? Can you "assemble" a software application by picking a routine here, a routine there, and expect the program to run? The answer is obviously no! Unlike hardware, it is very difficult to "assemble" an application from software components. Since the advent of computer 60 years ago, we have written tons and tons of programs. However, for each new application, we have to re-invent the wheels and write the program from scratch.
Why re-invent the wheels?
Traditional Procedural-Oriented languages
Can we do this in traditional procedural-oriented programming language such as C, Fortran, Cobol, or Pascal?
Traditional procedural-oriented languages [such as C and Pascal] suffer some notable drawbacks in creating reusable software components:
- The programs are made up of functions. Functions are often not reusable. It is very difficult to copy a function from one program and reuse in another program because the the function is likely to reference the headers, global variables and other functions. In other words, functions are not well-encapsulated as a self-contained reusable unit.
- The procedural languages are not suitable of high-level abstraction for solving real life problems. For example, C programs uses constructs such as if-else, for-loop, array, function, pointer, which are low-level and hard to abstract real problems such as a Customer Relationship Management [CRM] system or a computer soccer game. [Imagine using assembly codes, which is a very low level code, to write a computer soccer game. C is better but no much better.]
In brief, the traditional procedural-languages separate the data structures and algorithms of the software entities.
In the early 1970s, the US Department of Defense [DoD] commissioned a task force to investigate why its IT budget always went out of control; but without much to show for. The findings are:
- 80% of the budget went to the software [while the remaining 20% to the hardware].
- More than 80% of the software budget went to maintenance [only the remaining 20% for new software development].
- Hardware components could be applied to various products, and their integrity normally did not affect other products. [Hardware can share and reuse! Hardware faults are isolated!]
- Software procedures were often non-sharable and not reusable. Software faults could affect other programs running in computers.
The task force proposed to make software behave like hardware OBJECT. Subsequently, DoD replaces over 450 computer languages, which were then used to build DoD systems, with an object-oriented language called Ada.
Object-Oriented Programming Languages
Object-oriented programming [OOP] languages are designed to overcome these problems.
- The basic unit of OOP is a class, which encapsulates both the static attributes and dynamic behaviors within a "box", and specifies the public interface for using these boxes. Since the class is well-encapsulated [compared with the function], it is easier to reuse these classes. In other words, OOP combines the data structures and algorithms of a software entity inside the same box.
- OOP languages permit higher level of abstraction for solving real-life problems. The traditional procedural language [such as C and Pascal] forces you to think in terms of the structure of the computer [e.g. memory bits and bytes, array, decision, loop] rather than thinking in terms of the problem you are trying to solve. The OOP languages [such as Java, C++, C#] let you think in the problem space, and use software objects to represent and abstract entities of the problem space to solve the problem.
As an example, suppose you wish to write a computer soccer games [which I consider as a complex application]. It is quite difficult to model the game in procedural-oriented languages. But using OOP languages, you can easily model the program accordingly to the "real things" appear in the soccer games.
- Player: attributes include name, number, location in the field, and etc; operations include run, jump, kick-the-ball, and etc.
- Ball:
- Reference:
- Field:
- Audience:
- Weather:
Most importantly, some of these classes [such as
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }0 and
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }1] can be reused in another application, e.g., computer basketball game, with little or no modification.
Benefits of OOP
The procedural-oriented languages focus on procedures, with function as the basic unit. You need to first figure out all the functions and then think about how to represent data.
The object-oriented languages focus on components that the user perceives, with objects as the basic unit. You figure out all the objects by putting all the data and operations that describe the user's interaction with the data.
Object-Oriented technology has many benefits:
- Ease in software design as you could think in the problem space rather than the machine's bits and bytes. You are dealing with high-level concepts and abstractions. Ease in design leads to more productive software development.
- Ease in software maintenance: object-oriented software are easier to understand, therefore easier to test, debug, and maintain.
- Reusable software: you don't need to keep re-inventing the wheels and re-write the same functions for different situations. The fastest and safest way of developing a new application is to reuse existing codes - fully tested and proven codes.
OOP Basics
Classes & Instances
Class: A class is a definition of objects of the same kind. In other words, a class is a blueprint, template, or prototype that defines and describes the static attributes and dynamic behaviors common to all objects of the same kind.
Instance: An instance is a realization of a particular item of a class. In other words, an instance is an instantiation of a class. All the instances of a class have similar properties, as described in the class definition. For example, you can define a class called "
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }2" and create three instances of the class "
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }2" for "
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }4", "
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }5" and "
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }6".
The term "object" usually refers to instance. But it is often used quite loosely, which may refer to a class or an instance.
A Class is a 3-Compartment Box encapsulating Data and Functions
A class can be visualized as a three-compartment box, as illustrated:
- Classname [or identifier]: identifies the class.
- Data Members or Variables [or attributes, states, fields]: contains the static attributes of the class.
- Member Functions [or methods, behaviors, operations]: contains the dynamic operations of the class.
In other words, a class encapsulates the static attributes [data] and dynamic behaviors [operations that operate on the data] in a box.
Class Members: The data members and member functions are collectively called class members.
The followings figure shows a few examples of classes:
The following figure shows two instances of the class
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }2, identified as "
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }8" and "
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }9".
Unified Modeling Language [UML] Class and Instance Diagrams: The above class diagrams are drawn according to the UML notations. A class is represented as a 3-compartment box, containing name, data members [variables], and member functions, respectively. classname is shown in bold and centralized. An instance [object] is also represented as a 3-compartment box, with instance name shown as
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }0 and underlined.Brief Summary
- A class is a programmer-defined, abstract, self-contained, reusable software entity that mimics a real-world thing.
- A class is a 3-compartment box containing the name, data members [variables] and the member functions.
- A class encapsulates the data structures [in data members] and algorithms [member functions]. The values of the data members constitute its state. The member functions constitute its behaviors.
- An instance is an instantiation [or realization] of a particular item of a class.
Class Definition
In C++, we use the keyword
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }1 to define a class. There are two sections in the class declaration:
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 and
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }3, which will be explained later. For examples,
Class Naming Convention: A classname shall be a noun or a noun phrase made up of several words. All the words shall be initial-capitalized [camel-case]. Use a singular noun for classname. Choose a meaningful and self-descriptive classname. For examples,
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }4,
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }5,
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }6,
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }7 and
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }8.
Creating Instances of a Class
To create an instance of a class, you have to:
- Declare an instance identifier [name] of a particular class.
- Invoke a constructor to construct the instance [i.e., allocate storage for the instance and initialize the variables].
For examples, suppose that we have a class called
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9, we can create instances of
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 as follows:
Alternatively, you can invoke the constructor explicitly using the following syntax:
Dot [.] Operator
To reference a member of a object [data member or member function], you must:
- First identify the instance you are interested in, and then
- Use the dot operator [
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
1] to reference the member, in the form of1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
2.
For example, suppose that we have a class called
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9, with two data members [
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 255] and two functions [
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 256 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 257]. We have created three instances of the class
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9, namely,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 259,
double radius = 1.0; // error: ISO C++ forbids in-class initialization of non-const static member 'radius'0 and
double radius = 1.0; // error: ISO C++ forbids in-class initialization of non-const static member 'radius'1. To invoke the function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 257, you must first identity the instance of interest, says
double radius = 1.0; // error: ISO C++ forbids in-class initialization of non-const static member 'radius'0, then use the dot operator, in the form of
double radius = 1.0; // error: ISO C++ forbids in-class initialization of non-const static member 'radius'4, to invoke the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 257 function of instance
double radius = 1.0; // error: ISO C++ forbids in-class initialization of non-const static member 'radius'0.
For example,
Calling
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 257 without identifying the instance is meaningless, as the radius is unknown [there could be many instances of
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 - each maintaining its own radius].
In general, suppose there is a class called
double radius = 1.0; // error: ISO C++ forbids in-class initialization of non-const static member 'radius'9 with a data member called
Circle[double radius = 1.0, string color = "red"];0 and a member function called
Circle[double radius = 1.0, string color = "red"];1. An instance called
Circle[double radius = 1.0, string color = "red"];2 is constructed for
double radius = 1.0; // error: ISO C++ forbids in-class initialization of non-const static member 'radius'9. You use
Circle[double radius = 1.0, string color = "red"];4 and
Circle[double radius = 1.0, string color = "red"];5.
Data Members [Variables]
A data member [variable] has a name [or identifier] and a type; and holds a value of that particular type [as descried in the earlier chapter]. A data member can also be an instance of a certain class [to be discussed later].
Data Member Naming Convention: A data member name shall be a noun or a noun phrase made up of several words. The first word is in lowercase and the rest of the words are initial-capitalized [camel-case], e.g.,
Circle[double radius = 1.0, string color = "red"];6,
Circle[double radius = 1.0, string color = "red"];7,
Circle[double radius = 1.0, string color = "red"];8,
Circle[double radius = 1.0, string color = "red"];9 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 350. Take note that variable name begins with an lowercase, while classname begins with an uppercase.
Member Functions
A member function [as described in the earlier chapter]:
- receives parameters from the caller,
- performs the operations defined in the function body, and
- returns a piece of result [or void] to the caller.
Member Function Naming Convention: A function name shall be a verb, or a verb phrase made up of several words. The first word is in lowercase and the rest of the words are initial-capitalized [camel-case]. For example,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 256,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 352.
Take note that data member name is a noun [denoting a static attribute], while function name is a verb [denoting an action]. They have the same naming convention. Nevertheless, you can easily distinguish them from the context. Functions take arguments in parentheses [possibly zero argument with empty parentheses], but variables do not. In this writing, functions are denoted with a pair of parentheses, e.g.,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 353,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 257 for clarity.
Putting them Together: An OOP Example
A class called
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 is to be defined as illustrated in the class diagram. It contains two data members:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 [of type
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 357] and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 255 [of type
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 359]; and three member functions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 256,
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red01, and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 257.
Three instances of
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9s called
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 259,
double radius = 1.0; // error: ISO C++ forbids in-class initialization of non-const static member 'radius'0, and
double radius = 1.0; // error: ISO C++ forbids in-class initialization of non-const static member 'radius'1 shall then be constructed with their respective data members, as shown in the instance diagrams.
In this example, we shall keep all the codes in a single source file called
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red07.CircleAIO.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
To compile and run the program [with GNU GCC under Windows]:
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red
Constructors
A constructor is a special function that has the function name same as the classname. In the above
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 class, we define a constructor as follows:
A constructor is used to construct and initialize all the data members. To create a new instance of a class, you need to declare the name of the instance and invoke the constructor. For example,
A constructor function is different from an ordinary function in the following aspects:
- The name of the constructor is the same as the classname.
- Constructor has no return type [or implicitly returns
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red
09]. Hence, no> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red
10 statement is allowed inside the constructor's body. - Constructor can only be invoked once to initialize the instance constructed. You cannot call the constructor afterwards in your program.
- Constructors are not inherited [to be explained later].
Default Arguments for Functions
In C++, you can specify the default value for the trailing arguments of a function [including constructor] in the function header. For example,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
"public" vs. "private" Access Control Modifiers
An access control modifier can be used to control the visibility of a data member or a member function within a class. We begin with the following two access control modifiers:
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red
11: The member [data or function] is accessible and available to all in the system.> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red
12: The member [data or function] is accessible and available within this class only.
For example, in the above
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 definition, the data member
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 is declared
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2. As the result,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 is accessible inside the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 class, but NOT outside theclass. In other words, you cannot use "
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red18" to refer to
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 259's
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 in
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red21. Try inserting the statement "
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red22" in
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red21 and observe the error message:
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private
Try moving
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 to the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }3 section, and re-run the statement.
On the other hand, the function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 256 is declared
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }3 in the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 class. Hence, it can be invoked in the
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red21.
UML Notation: In UML notation,
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }3 members are denoted with a "
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red31", while
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 members with a "
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red33" in the class diagram.
Information Hiding and Encapsulation
A class encapsulates the static attributes and the dynamic behaviors into a "3-compartment box". Once a class is defined, you can seal up the "box" and put the "box" on the shelve for others to use and reuse. Anyone can pick up the "box" and use it in their application. This cannot be done in the traditional procedural-oriented language like C, as the static attributes [or variables] are scattered over the entire program and header files. You cannot "cut" out a portion of C program, plug into another program and expect the program to run without extensive changes.
Data member of a class are typically hidden from the outside word, with
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 access control modifier. Access to the private data members are provided via
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }3 assessor functions, e.g.,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 256 and
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red01.
This follows the principle of information hiding. That is, objects communicate with each others using well-defined interfaces [public functions]. Objects are not allowed to know the implementation details of others. The implementation details are hidden or encapsulated within the class. Information hiding facilitates reuse of the class.
Rule of Thumb: Do not make any data member
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }3, unless you have a good reason.
Getters and Setters
To allow other to read the value of a
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 data member says
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red40, you shall provide a get function [or getter or accessor function] called
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red41. A getter need not expose the data in raw format. It can process the data and limit the view of the data others will see. Getters shall not modify the data member.
To allow other classes to modify the value of a
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 data member says
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red40, you shall provide a set function [or setter or mutator function] called
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red44. A setter could provide data validation [such as range checking], and transform the raw data into the internal representation.
For example, in our
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 class, the data members
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 255 are declared
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2. That is to say, they are only available within the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 class and not visible outside the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 class - including
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red21. You cannot access the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 data members
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 255 from the
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red21 directly - via says
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red18 or
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red57. The
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 class provides two public accessor functions, namely,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 256 and
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red01. These functions are declared
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }3. The
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red21 can invoke these public accessor functions to retrieve the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 255 of a
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 object, via says
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red66 and
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red67.
There is no way you can change the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 or
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 255 of a
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 object, after it is constructed in
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red21. You cannot issue statements such as
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red72 to change the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 of instance
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 259, as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 is declared as
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 in the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 class and is not visible to other including
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red21.
If the designer of the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 class permits the change the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 255 after a
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 object is constructed, he has to provide the appropriate setter, e.g.,
With proper implementation of information hiding, the designer of a class has full control of what the user of the class can and cannot do.
Keyword "this"
You can use keyword "
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red83" to refer to this instance inside a class definition.
One of the main usage of keyword
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red83 is to resolve ambiguity between the names of data member and function parameter. For example,
In the above codes, there are two identifiers called
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 - a data member and the function parameter. This causes naming conflict. To resolve the naming conflict, you could name the function parameter
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red86 instead of
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254. However,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254 is more approximate and meaningful in this context. You can use keyword
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red83 to resolve this naming conflict. "
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red90" refers to the data member; while "
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 254" resolves to the function parameter.
"
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red83" is actually a pointer to this object. I will explain pointer and the meaning of "
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red93" operator later.
Alternatively, you could use a prefix [such as
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red94] or suffix [such as
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red95] to name the data members to avoid name crashes. For example,
C++ Compiler internally names their data members beginning with a leading underscore [
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red96] and local variables with 2 leading underscores [e.g.,
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red97]. Hence, avoid name beginning with underscore in your program.
"const" Member Functions
A
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red98 member function, identified by a
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red98 keyword at the end of the member function's header, cannot modifies any data member of this object. For example,
Convention for Getters/Setters and Constructors
The constructor, getter and setter functions for a
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 data member called
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red40 of type
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2102 in a class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2103 have the following conventions:
For a
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2104 variable
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red40, the getter shall be named
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2106, instead of
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red41, as follows:
Default Constructor
A default constructor is a constructor with no parameters, or having default values for all the parameters. For example, the above
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9's constructor can be served as default constructor with all the parameters default.
If C++, if you did not provide ANY constructor, the compiler automatically provides a default constructor that does nothing. That is,
Compiler will not provide a default constructor if you define any constructor[s]. If all the constructors you defined require arguments, invoking no-argument default constructor results in error. This is to allow class designer to make it impossible to create an uninitialized instance, by NOT providing an explicit default constructor.
Constructor's Member Initializer List
Instead of initializing the private data members inside the body of the constructor, as follows:
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
We can use an alternate syntax called member initializer list as follows:
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }
Member initializer list is placed after the constructor's header, separated by a colon [
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2109]. Each initializer is in the form of
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2110. For fundamental type, it is equivalent to
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2111. For object, the constructor will be invoked to construct the object. The constructor's body [empty in this case] will be run after the completion of member initializer list.
It is recommended to use member initializer list to initialize all the data members, as it is often more efficient than doing assignment inside the constructor's body.
*Destructor
A destructor, similar to constructor, is a special function that has the same name as the classname, with a prefix
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2112, e.g.,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2113. Destructor is called implicitly when an object is destroyed.
If you do not define a destructor, the compiler provides a default, which does nothing.
Advanced Notes- If your class contains data member which is dynamically allocated [via
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
14 or1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
15 operator], you need to free the storage via1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
16 or1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
17.
*Copy Constructor
A copy constructor constructs a new object by copying an existing object of the same type. In other words, a copy constructor takes an argument, which is an object of the same class.
If you do not define a copy constructor, the compiler provides a default which copies all the data members of the given object. For example,
The copy constructor is particularly important. When an object is passed into a function by value, the copy constructor will be used to make a clone copy of the argument.
Advanced Notes- Pass-by-value for object means calling the copy constructor. To avoid the overhead of creating a clone copy, it is usually better to pass-by-reference-to-
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red
98, which will not have side effect on modifying the caller's object. - The copy constructor has the following signature:
- The default copy constructor performs shadow copy. It does not copy the dynamically allocated data members created via
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
14 or1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
15 operator.
*Copy Assignment Operator [=]
The compiler also provides a default assignment operator [
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2121], which can be used to assign one object to another object of the same class via memberwise copy. For example, using the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 class defined earlier,Advanced Notes
- You could overload the assignment opeator to override the default.
- The copy constructor, instead of copy assignment operator, is used in declaration:
- The default copy assignment operator performs shadow copy. It does not copy the dynamically allocated data members created via
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
14 or1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
15 operator. - The copy assignment operator has the following signature:
- The copy assignment operator differs from the copy constructor in that it must release the dynamically allocated contents of the target and prevent self assignment. The assignment operator shall return a reference of this object to allow chaining operation [such as
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
25]. - The default constructor, default destructor, default copy constructor, default copy assignment operators are known as special member functions, in which the compiler will automatically generate a copy if they are used in the program and not explicitly defined.
Separating Header and Implementation
For better software engineering, it is recommended that the class declaration and implementation be kept in 2 separate files: declaration is a header file "
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2126"; while implementation in a "
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2127". This is known as separating the public interface [header declaration] and the implementation. Interface is defined by the designer, implementation can be supplied by others. While the interface is fixed, different vendors can provide different implementations. Furthermore, only the header files are exposed to the users, the implementation can be provided in an object file "
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2128" [or in a library]. The source code needs not given to the users.
I shall illustrate with the following examples.
Example: The Circle Class
Instead of putting all the codes in a single file. We shall "separate the interface and implementation" by placing the codes in 3 files.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
29: defines the public interface of theCircle[double r = 1.0, string c = "red"] : radius[r], color[c] { }
9 class.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
31: provides the implementation of theCircle[double r = 1.0, string c = "red"] : radius[r], color[c] { }
9 class.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
33: A test driver program for theCircle[double r = 1.0, string c = "red"] : radius[r], color[c] { }
9 class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Program Notes:
- The header file contains declaration statements, that tell the compiler about the names and types, and function prototypes without the implementation details.
- C++98/03 does NOT allow you to assign an initial value to a data member [except
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red
981 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
36 members]. Date members are to be initialized via the constructor. For example,double radius = 1.0; // error: ISO C++ forbids in-class initialization of non-const static member 'radius'
C++11 allows in-class initialization of data members. - You can provide default value to function's arguments in the header. For example,
Circle[double radius = 1.0, string color = "red"];
- Header contains function prototype, the parameter names are ignored by the compiler, but good to serve as documentation. For example, you can leave out the parameter names in the prototype as follows:
Header files shall contains constants, function prototypes, class/struct declarations.
Circle.cpp - Implementation1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
Program Notes:
- The implementation file provides the definition of the functions, which are omitted from the declaration in the header file.
- #include "Circle.h"
The compiler searches the headers in double quotes [such as1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
37] in the current directory first, then the system's include directories. For header in angle bracket [such as1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
38], the compiler does NOT searches the current directory, but only the system's include directories. Hence, use double quotes for user-defined headers. - Circle::Circle[double r, string c] {
You need to include the1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
39 [called class scope resolution operator] in front of all the members names, so as to inform the compiler this member belong to a particular class.
[Class Scope: Names defined inside a class have so-called class scope. They are visible within the class only. Hence, you can use the same name in two different classes. To use these names outside the class, the class scope resolution operator1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
39 is needed.] - You CANNOT place the default arguments in the implementation [they shall be placed in the header]. For example,
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red
0
You can compile the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2131 to an object file called
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2142, via option
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2143 [compile-only] in GNU GCC:
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red1
To use the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 class, the user needs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2129 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2142. He does not need
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2131. In other words, you do not need to give away your source codes, but merely the public declarations and the object codes.TestCircle.cpp - Test Driver
Let's write a test program to use the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }9 class created.
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red2Compiling the Test Program
To compile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2133 with the object code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2142 [and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2129]:
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red3
You can also compile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2133 with the source code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2131 [and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2129]
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red4
Let's write a class called
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2155, which models a specific instance of time with hour, minute and second values, as shown in the class diagram.
The class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2155 contains the following members:
- Three
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }
2 data members:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
58 [0-23],1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
59 [0-59] and1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
60 [0-59], with default values of 0. - A
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }
3 constructor1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
62, which initializes the data members1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
58,1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
59 and1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
60 with the values provided by the caller. Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }
3 getters and setters for private data members:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
67,1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
68,1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
69,1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
70,1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
71, and1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
72.- A
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }
3 member function1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
74 to set the values of ,1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
59 and1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
60 given by the caller. - A
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }
3 member function1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
78 to print this1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
55 instance in the format "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
80", zero-filled, e.g.,1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
81. - A
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }
3 member function1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
83, which increase this instance by one second.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
83 of1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
85 shall be1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
86.
Let's write the code for the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2155 class, with the header and implementation separated in two files:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2188 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2189.Header - Time.h
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red5Dissecting Time.h
#ifndef TIME_H
#define TIME_H
......
#endif
To prevent an header file from included more than once into a source file [which could result in compilation error if an entity is declared twice, e.g.,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2190], we wrap the header codes within a pair of preprocessor directives
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2191 [if not define] and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2192. The codes within the if-block will only be included if the identifier
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2193 has not been defined. This is true for the first inclusion, which also defines the identifier
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2193 [the first directive in body of the if-block]. No subsequent inclusion is possible, since
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2193 has been defined during the first inclusion. By convention, use the identifier
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2196 [or
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2197] for header
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2198.
class Time {
private:
......
public:
......
};
The header
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2188 contains the class declaration for the class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2155. It is divided into two sections:
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 and
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }3. The
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 members [data or functions] are accessible by members of this class only, while
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }3 members are visible by all [such as the
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red21 function which is outside the class]. The class declaration must be terminated by a semicolon.
private:
int hour;
int minute;
int second;
public:
......
We declare 3 private data members called
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2158,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2159 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2160. In C++98/C++03, you are NOT allow to initialize a data member in the class declaration [except
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red98
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2136
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private11 data members]. For example, setting
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private12 causes a compilation error. Instead, the data members are to be initialized in the constructor [to be shown later]. The newer C++11 allows initialization of data members.
Only member function prototypes are listed in the class declaration. A function prototype consists of the return-type, function name and parameter types.
Time[int h = 0, int m = 0, int s = 0];
declares the so-called constructor. A constructor is a special function that has the same name as the class. A constructor has no return type, or implicitly return
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red09. No
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red10 statement is allowed inside the constructor's body. A constructor can only be used during the instance declaration to initialize the data members of the instance. It cannot be invoked thereafter.
In the function prototypes of the header, we can set the default values of the function's parameters for any function member using "
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private15". In this case, this constructor can be invoked with 0 to 3 arguments, the omitted trailing arguments will be set to their default values, e.g.,
The identifiers
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private16,
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private17 and
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private18 are not needed in the function prototype - you only need to specify the parameters' types. But they serve as proper documentation, and are strongly recommended.
int getHour[] const;
void setHour[int h];
int getHour[] const;
void setHour[int h];
int getHour[] const;
void setHour[int h];
declare the so-called getter and setter for the private data member
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2158,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2159 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2160. Since the data members are
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 and are not accessible outside the class,
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }3 getters and setters are often provided to read and modify the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 data members. By convention, a getter receives nothing [
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red09] from the caller and returns a value of the type of the data member; a setter receives a value of the type of the data member and returns
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red09. Setters may validate the input before setting the value of the data member.
We declare the getter function constant, by placing the keyword
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red98 after the function parameter list. A
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red98 member function cannot modify any data member of this object. Getter does not need to modify any data member.
void setTime[int h, int m, int s];
declares a public member function to set the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2158,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2159 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2160 of this instance in one call.
void print[] const;
declares a public member function to print this instance in the format
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private32, zero-filled, e.g.,
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private33. The function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2178 returns
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red09.
void nextSecond[];
declares a public member function to increase this instance by one second. For example,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2185 becomes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2186. The function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2183 returns
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red09.Implementation - Time.cpp
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red6Dissecting Time.cpp
The implementation file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2189 contains member's definitions [whereas the header file contains the declarations], in particular, member functions.
All member's identifiers in the implementation are preceded by the classname and the scope resolution operator [
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private41], e.g.,
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private42 and
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private43, so that the compiler can tell that these identifiers belong to a particular class, in this case,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2155.
Time::Time[int h, int m, int s] {
hour = h;
minute = m;
second = s;
}
In the constructor, we initialize the
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 data members
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2158,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2159 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2160 based on the inputs provided by the caller. C++ does NOT initialize fundamental-type [e.g.,
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private11,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 357] data members. It also does NOT issue an error message if you use an data member before it is initialized. Hence, It is strongly recommended to initialize all the data members in the constructor, so that the constructed instance is complete, instead of relying on the user to set the values of the data members after construction.
The default values of the parameters are specified in the class declaration [in the header], NOT in the function definition. Placing a default value in function definition [e.g.,
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private51] causes a compilation error.
Take note that we have not included input validation [e.g., hour shall be between 0 and 23] in the constructor [and setters]. We shall do that in the later example.
int Time::getHour[] const {
return hour;
}
the public getter for private data member
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2158 simply returns the value of the data member
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2158.
void Time::setHour[int h] {
hour = h;
}
the public setter for private data member
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2158 sets the data member
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2158 to the given value
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private16. Again, there is no input validation for h [shall be between 0 to 23].
The rest of the function definitions are self-explanatory.
"this" PointerInstead of naming the function parameters
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private16,
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private17 and
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private18, we would like to name the parameters
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2158,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2159 and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2160, which are semantically more meaningful. However, these names crashes with the names of private data members. C++ provides a keyword
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red83 [which is a pointer to this instance - to be discussed later] to differentiate between the data members and function parameters.
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private64,
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private65 and
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private66 refer to the data members; while
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2158,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2159, and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2160 refer to the function parameters. We can rewrite the constructor and setter as follows:Member Initializer List
C++ provide an alternative syntax to initialize data members in the constructor called member initializer list. For example,
The member initializer list is placed after the function parameter list, separated by a colon, in the form of
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private70. For fundamental-type data members [e.g.,
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private11,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 357],
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private73 is the same as
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private74. For object data members [to be discussed later], the copy constructor will be invoked. The function body will be executed after the member initializer list, which is empty in this case.
The data members in the initializer list are initialized in the order of their declarations in the class declaration, not the order in the initializer list.
Test Driver - TestTime.cpp> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red7Dissecting TestTime.cpp
The test driver tests the constructor [with and without the default values] and all the public member functions. Clearly, no input validation is carried out, as reflected in instance
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private75.Exercise
Add member functions
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private76,
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private77,
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private78,
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private79,
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private80 to the
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2155 class.Compiling the Program
You can compile all the source file together to get the executable file as follows:
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red8
Alternatively, you can compile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 2189 into an object file
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private83, and then the test driver with the object file. In this way, you only distribute the object file and header file, not the source file.
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red9
Example: The Point Class
The
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private84 class, as shown in the class diagram, models 2D points with x and y co-ordinates.
In the class diagram, "
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red33" denotes
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }2 member; "
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red31" denotes
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }3 member. "
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private89" specifies the default value of a data member.
The
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private84 class contains the followings:
- Private data members
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private
91 andCircleAIO.cpp:8:11: error: 'double Circle::radius' is private
92 [of typeCircleAIO.cpp:8:11: error: 'double Circle::radius' is private
11], with default values of 0. - A constructor, getters and setters for private data member
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private
91 andCircleAIO.cpp:8:11: error: 'double Circle::radius' is private
92. - A function setXY[] to set both
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private
91 andCircleAIO.cpp:8:11: error: 'double Circle::radius' is private
92 coordinates of aCircleAIO.cpp:8:11: error: 'double Circle::radius' is private
84. - A function
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private
99 which returnsCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
00. You can use the built-inCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
01 function in1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
38 to compute the square root. - A function
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
03 which returnsCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
04. You can use the built-inCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
05 function in1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
38 to compute the gradient in radians. - A function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
78 which prints "Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
08" of this instance.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25Point.cpp - Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 211TestPoint.cpp - Test Driver
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Example: The Account Class
A class called
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }09, which models a bank account, is designed as shown in the class diagram. It contains:
- Two private data members:
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
10 [CircleAIO.cpp:8:11: error: 'double Circle::radius' is private
11] andCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
12 [1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
7], which maintains the current account balance. - Public functions
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
14 andCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
15, which adds or subtracts the given amount from the balance, respectively. TheCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
15 function shall print "amount withdrawn exceeds the current balance!" ifCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
17 is more thanCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
12. - A public function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
78, which shall print "A/C no: xxx Balance=xxx" [e.g., A/C no: 991234 Balance=$88.88], withCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
12 rounded to two decimal places.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 213Implementation file - Account.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 214Test Driver - TestAccount.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
Example: The Ball class
A
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }0 class models a moving ball, designed as shown in the class diagram, contains the following members:
- Four
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }
2 data membersCircleAIO.cpp:8:11: error: 'double Circle::radius' is private
91,CircleAIO.cpp:8:11: error: 'double Circle::radius' is private
92,Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
25 andCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
26 to maintain the position and speed of the ball. - A constructor, and public getters and setters for the private data members.
- A function
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
27, which sets the position of the ball andCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
28 to set the speed of the ball. - A function
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
29, which increasesCircleAIO.cpp:8:11: error: 'double Circle::radius' is private
91 andCircleAIO.cpp:8:11: error: 'double Circle::radius' is private
92 byCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
25 andCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
26, respectively. - A function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
78, which prints "Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
35", to 2 decimal places.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 216Implementation File - Ball.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 217Test Driver - TestBall.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 218
Let's start with the Author class
Let's begin with a class called
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }36, designed as shown in the class diagram. It contains:
- Three
Circle[double r = 1.0, string c = "red"] : radius[r], color[c] { }
2 data members:Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
38 [Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
39],Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
40 [Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
39], andCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
42 [Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
43 ofCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
44,Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
45 orCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
46 for unknown]. - A constructor to initialize the
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
38,Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
40 andCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
42 with the given values. There are no default values for data members. - Getters for
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
38,Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }
40 andCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
42, and setter forCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
40. There is no setter forCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
38 andCircle[double r = 1.0, string c = "red"] { radius = r; color = c; }
42 as we assume that these attributes cannot be changed. - A
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
78 member function that prints "name [gender] at email", e.g., "Peter Jones [m] at peter@somewhere.com".
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 219Implementation File - Author.cpp
CircleAIO.cpp:8:11: error: 'double Circle::radius' is private0Dissecting the Author.cpp
Author::Author[string name, string email, char gender] {
this->name = name;
setEmail[email];
In this example, we use identifier
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }38 in the function's parameter, which crashes with the data member's identifier
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }38. To differentiate between the two identifiers, we use the keyword
> g++ -o CircleAIO.exe CircleAIO.cpp // -o specifies the output file name > CircleAIO Radius=1.2 Area=4.5239 Color=blue Radius=3.4 Area=36.3169 Color=red Radius=1 Area=3.1416 Color=red83, which is a pointer to this instance.
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }60 refers to the data member; while
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }38 refers to the function's parameter.
No input validation is done on the parameter
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }38. On the other hand, for
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }40, we invoke setter
Circle[double r = 1.0, string c = "red"] { radius = r; color = c; }64 which performs input validation.
if [gender == 'm' || gender == 'f'] {
this->gender = gender;
} else {
cout email = email;
} else {
cout 0] {
this->price = price;
} else {
cout