Classes
C++ is an object-oriented language and programmers can use existing classes or create their own classes in their program. Class creation is very similar to class creation in Java. It is also somehow related to struct in the C programming language. In C, a struct is a collection of attributes without any object-oriented feature - it has no constructor, no method, and no inheritance. A class in C++ is also a collection of attributes, but it also implements all object-oriented features. It has a constructor, methods, it can inherit from other classes and it allows encapsulation with access keywords.
The Point Class Example
A simple illustration of this concept is given with the Point class, which defines a data type representing a point in 2-D space.
class Point {
public:
// constructors
Point() : _x(0), _y(0) { }
Point(float x, float y) : _x(x), _y(y) { }
// public methods
void move(float dx, float dy) {
_x += dx;
_y += dy;
}
float x() const { return _x; }
float y() const { return _y; }
private:
// private data fields
float _x;
float _y;
};
int main() {
return 0;
}
Both constructors include a member initialization list, with the : colon character followed by a comma-separated list of member initializers. In this example, the two class attributes _x and _y are initialized with either the 0 value or with the values passed as arguments to the constructor.
Key Points About C++ Classes
Based on the Point class example, it is important to note the following points:
- A class may define one or several constructors which are called upon object creation. These constructors may have no, one or several parameters and may thus be overloaded - like any class method. Overloaded constructors or methods differ in the number and/or type of parameters.
- One of the constructors is called each time an object is created on the stack or on the heap.
- Unlike Java, access modifiers are defined by section and not individually for each method or data members.
- In C++, accessor (or “getter”) methods are tagged with the keyword
const, which prevents the method from modifying instance variables. - In C++, there is a semicolon after the class’s closing bracket.
More details are given later in this course on the C++ class concept, including on inheritance and template classes.
Using Classes
In C++, we can choose if the object is created on the stack (without the new keyword) or on the heap with the new keyword.
When an object is on the stack, it exists until the scope (of the method or function) is left. When an object is on the heap, it exists until the delete keyword is called.
#include <iostream>
class Point {
public:
// constructors
Point() : _x(0), _y(0) { }
Point(float x, float y) : _x(x), _y(y) { }
// public methods
void move(float dx, float dy) {
_x += dx;
_y += dy;
}
float x() const { return _x; }
float y() const { return _y; }
private:
// private data fields
float _x;
float _y;
};
int main() {
// Initialization of a point on the stack
Point p1;
std::cout << "p(" << p1.x() << ", " << p1.y() << ")" << std::endl;
// Initialization of a point on the stack with the given values
Point p2(1, 2);
std::cout << "p(" << p2.x() << ", " << p2.y() << ")" << std::endl;
// Initialization of a point on the heap
Point* p3 = new Point();
std::cout << "p(" << p3->x() << ", " << p3->y() << ")" << std::endl;
// Initialization of a point on the heap with the given values
Point* p4 = new Point(3, 4);
std::cout << "p(" << p4->x() << ", " << p4->y() << ")" << std::endl;
// Delete objects on the heap and reset the pointer values to nullptr
delete p3;
p3 = nullptr;
delete p4;
p4 = nullptr;
return 0;
}
Overloading operators
In C++, unlike in Java, operators may be overloaded when declaring a class. Thus, a programmer can use operators with user-defined classes/types. These operators are defined with the keyword operator followed by the symbol used for the operator being defined. Like any other method, an operator has a return type and a parameter list. In other words, C++ operators are methods with special names.
Operators that are commonly overloaded in a class are:
- the copy assignment operator defined as
T& operator=(const T& other), expected to perform no action on self assignment and to return the lhs of the operation by reference. - comparison operators such as
bool operator==(const T&, const T&)for comparing two instances of the class. - user-defined conversion function written as operator conversion-type-id such as operator
int()for converting an instance of the class to an int value.
The program example illustrates the overloading of different operators for a class that represents rational numbers.
#include <cstdint>
#include <iostream>
class RationalNumber
{
public:
RationalNumber(int32_t n, int32_t d = 1) :
n(n/gcd(n, d)), d(d/gcd(n,d)) {
}
int32_t num() const {
return n;
}
int32_t den() const {
return d;
}
// define the *= operator
RationalNumber& operator*=(const RationalNumber& rhs)
{
int new_n = n * rhs.n / gcd(n * rhs.n, d * rhs.d);
d = d * rhs.d / gcd(n * rhs.n, d * rhs.d);
n = new_n;
return *this;
}
// define the == operator
bool operator==(const RationalNumber& rhs)
{
return num() == rhs.num() && den() == rhs.den();
}
private:
constexpr int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
int32_t n;
int32_t d;
};
// define the << operator for RationalNumber
std::ostream& operator<<(std::ostream& out, const RationalNumber& f)
{
return out << f.num() << '/' << f.den();
}
// define the operator for accomplishing multiplication of two rational numbers
RationalNumber operator*(RationalNumber lhs, const RationalNumber& rhs)
{
return lhs *= rhs;
}
int main() {
RationalNumber f1(3, 8);
RationalNumber f2(1, 2);
RationalNumber f3(10, 2);
std::cout << f1 << " * " << f2 << " = " << f1 * f2 << std::endl
<< f2 << " * " << f3 << " = " << f2 * f3 << std::endl;
std::cout << (f3 == f2 * 10) << std::endl;
}