Classes are an expanded concept of data structures : like data structures, they can contain data members, but they can also contain funcitons as members.
An object is an instantiation of a class. In terms of variables, a class would be the type, and an object would be the variable.
After the declarations of Rectangle
and rect
, any of the public members of object rect
can be accessed as if they were normal functions or normal variables, by simply inserting a dot (.) between object name and member name. This follows the same syntax as accessing the members of plain data structures.
The operator of scope (::)
is used to specify that the function being defined is a member of the class and not a regualr non-member function.
The most important property of a class is that it is a type
, and as such, we can declare multiple objects
of it.
Classes allow programming using object-oriented paradigms.
Classes are defined using either keyword class
or keyword struct
, with the following syntax:
class class_name {
access_specifier_1:
member1;
access_specifier_2;
member2;
...
} object_names;
Where class_name
is a valid identifier for the class
, object_names
is an optional list of names for objects of this class. The body of the declaration can contain members, which can either be data or function declarations, and opetianlly access specifiers.
Classes have the same format as plain data structures, except that they can also include functions
and have these new things called access specifiers
.
An access specifier is one of the following three keywords:
members of a class are accessible only from within other members of the same class
members are accessible from other members of the same class (or from their “friends”), but also form members of their derived classes
members are accessible from anywhere where the object is visible
By default, all members of a class declared with the class
keyword have private access for all its members. Therefore, any member that is declared before any other access specifier has private access automatically. For example:
class Rectangle {
int width, height;
public:
void set_values (int, int);
int area(void);
} rect;
Declares a *class* (i.e., a type) called `Rectangle` and an *object* (i.e., a variable) of this class, called `rect`. This class contains four members: two data members of type `int` (member `width` and member `height`) with *private access* (because *private* is the default access level) and two member functions with *public access*: the functions `set_values` and `area`, of which for now we have only included their *declaration*, but not their *definiton*.
Notice the difference between the *class name* and the object name: In the previous example , Rectangle was the class name (i.e., the type), whereas `rect` was an object of type `Rectangle`.
The way of calling constructors by enclosing their arguments in parentheses, as shown above, is known as functional form. But constructors can also be called with other syntaxes:
First, constructors with a single parameter can be called using the variable initialization syntax (an equal sign followed by the argument)
class_name object_name = initialization_value;
More recently, C++ introduced the possibility of consructors to be called using uniform initialization, which essentially is the same as the functional form, but using braces ({}) instead of parentheses ( () ):
class_name object_name {value, value, value, ...}
Optionally, this last syntax can include an equal
sign before the braces.
// classes and uniform initialization
#include
using namespace std;
class Circle {
double radius;
public:
Circle (double r) {radius = r;}
double circum() {return 2*radius*3.14159265;}
};
int main() {
Circle foo (10); //functional form
Circle bar = 20; // assignment init
Circle baz {30}; // uniform init
Circle qux = {40}; // POD-like
cout << "foo's circumference: " << foo.circum() << '\n';
return 0;
}
An advantage of uniform initialization over functional form is that, unlike parentheses, braces cannot be confused with function declarations, and thus can be used to explicitly call default constructors:
Rectangle rectb; // default constructor called
Rectangle rectc(); // function declaration (default constructor NOT called)
Rectangle rectd{}; //default constructor called
The choice of syntax to call constructors is largely a matter of style. Most existing code currently uses functional form, and some newer style guides suggest to choose uniform initialization over the others, even though it also has its potential pitfalls for its preference of initializer_list as its type.
When a constructor is used to initialize other members, these other members can be initialized directly, without resorting to statements in its body. This is done by inserting, before the constructor’s body, a colon (:
) and a list of initializations for class members. For example, consider a class with the following declaration:
class Rectangle {
int width, height;
public:
Rectangle (int, int);
int area() {return width*height;}
};
Rectangle::Rectangle (int x, int y) {width=x; height=y;}
// is equal to
Rectangle::Rectangle (int x, int y) : width(x) {height=y;} // member initialization
// is equal to
Rectangle::Rectangle (int x, int y) : width(x), height(y) {}
In the last case, the constructor does nothing else than initialize
its members, hence it has an empty function body.
For members of fundamental types, it makes no difference which of the ways above the constructor is defined, because they are not initialized by default, but for member objects (those whose type is a class), if they are not initialized after the colon, they are default-constructed
.
Objects can also be pointed to by pointers: Once declared, a class becomes a valid type, so it can be used as the type pointed to by a pointer. For example:
Rectangle * prect;
is a pointer to an object of class Rectangle
. Similarly as with plain data structures, the members of an object can be accessed directly from a pointer by using the arrow operator (->)
. Several operators to operate on objects and pointers (operatores *
, &
, .
, ->
, []
), they can be interpreted as :
Expression | Can be read as |
---|---|
*x |
pointed to by x (pointer) |
&x |
address to x (variable) |
x.y |
member y of object x |
x->y |
member y of object pointed to by x (pointer), arrow operator (->) |
(*x).y |
member y of object pointed to by x (equivalent to the previous one) |
x[0] |
first object pointed to by x |
x[1] |
second object pointed to by x |
x[n] |
(n+1)th object pointed to by x , offset operator [] |
Classes can be defined not only with keyword class
, but also with keyword struct
and union
.
The keyword struct
, generally used to declare plain data structures, can also be used to declare classes that have member functions, with the same syntax as with keyword class
. The only difference between both is that members of classes declared with the keyword struct
have public access by default, while members of classes declared with the keyword class
have private access by default. For all other purposes both keywords are equivalent in this context.
Conversely, the concept of unions
is different from that of classes declared with struct
and class
, since unions only store one data member at a time, but nevertheless they are also classes and thus also hold member functions. The default access in union
classes is public.