• Nem Talált Eredményt

More about classes

In document Mechatronic Systems Programming in C++ (Pldal 186-193)

III. fejezet - Object-oriented programming in C++

2. Classes and objects

2.2. More about classes

};

In normal member functions, the pointer this is declared as Classtype* const this, and in constant member functions as const Classtype*const this.

2.2. More about classes

In the previous subchapter, we arrived to classes from structures. We learned how to create well useable classes.

Now, it is time to solve tasks with the help of classes.

The present subchapter deals with less general knowledge about classes. This subchapter is mainly dedicated to those who already has some practice in how to create classes.

2.2.1. Static class members

In C++, the keyword static can be typed before the data members of classes in order that the objects of these classes could share these members (just like member functions). A static data member that is created in only one instance belongs directly to the class; therefore it is available for it even if there are no objects for that class.

A static data member should not be initialised within its class (independently of its access restriction). An exception to that rule is constituted by data members of type static const integer and enumeration to which a value can be assigned even within the class.

If a static data member is public, then it can be used anywhere in the program code by the name of the class and the scope operator (::). Otherwise, only instances of the class can access these members.

Besides presenting the usage of static members, the following example also demonstrates how to insert constants (static const and enum) in classes. Our mathematics class defined by ourselves (Math) makes it possible for us to call Sin() and Cos() member functions with data in radians or in degrees:

#include <iostream>

static double dDegree2Radian;

static Unit eMode;

static void MeasureUnit(Unit mode = radian) {

eMode = mode; } void PrintOutPI() { cout.precision(18); cout<<Pi<<endl;}

};

// creating and initialising static members const double Math::Pi = 3.141592653589793238462;

double Math::dDegree2Radian = Math::Pi/180;

Math::Unit Math::eMode = Math::radian;

As it can be seen in the example, an enumeration can also be placed within a class. The type name Unit and the enumerated constants (degree, radian) can be referenced with a name qualified with the class name. These names have class level scope, independently of the type name preceded by the keyword enum (Math::Unit):

Math::radian, Math::degree.

To manage static data members, we use static member functions in general (Math::Sin(), Math::Cos(), Math::MeasureUnit()). However, normal data members cannot be accessed from static functions since the pointer this does not figure among their parameters. The static members of a class can be accessed from non-static member functions without restrictions.

A possible usage of the class Math is presented in the following example:

int main() {

double y = Math::Sin(Math::Pi/6); // counts in radian Math::MeasureUnit(Math::degree); // counts in degrees y = Math::Sin(30);

Math::MeasureUnit(Math::radian); // counts in radian y = Math::Sin(Math::Pi/6);

Math m; // class instance m.MeasureUnit(Math::degree); // or

m.MeasureUnit(m.degree);

y = m.Sin(30);

m.MeasureUnit(m.radian); // or m.MeasureUnit(Math::radian);

y = m.Sin(Math::Pi/6);

m.PrintOutPI();

}

2.2.2. How to structure classes

The rules of C++ make it possible to structure classes in many ways. In the following examples, each of these methods is strictly separated but in the everyday practice we use them in different combinations.

2.2.2.1. Implicit inline member functions

In the first subchapter, the whole definition of member functions is placed in the description of a class.

Compilers consider these member functions automatically as inline functions. A big advantage of the solution is that the whole class can be stored in a header file, and the members of the class are logically grouped together.

In general, this solution is useful for small-sized classes.

As an example, let's see the class Point, which is able to handle points in the plane.

class Point { private:

int x,y;

public:

Point(int a = 0, int b = 0) { x = a; y = b; } int GetX() const { return x; }

int GetY() const { return y; } void SetX(int a) { x = a; } void SetY(int a) { y = a; }

void Move(int a, int b) { x = a; y = b; } void Move(const Point& p) { x = p.x; y = p.y; }

void PrintOut() const { cout<<"("<<x<<","<<y<<")\n"; } };

2.2.2.2. Class structures in C++/CLI applications

The .NET project of Visual C++, Java and C# languages follow principles similar to those above. But there is a big difference: class members are not grouped on the basis of their accessibility, so the accessibility can be provided to each member separately.

class Point {

private: int x,y;

public: Point(int a = 0, int b = 0) { x = a; y = b; } public: int GetX() const { return x; }

public: int GetY() const { return y; } public: void SetX(int a) { x = a; } public: void SetY(int a) { y = a; }

public: void Move(int a, int b) { x = a; y = b; } public: void Move(const Point& p) { x = p.x; y = p.y; }

public: void PrintOut() const { cout<<"("<<x<<","<<y<<")\n"; } };

2.2.2.3. Storing member functions in separate modules

It is easier to manage a bigger-sized class if its member functions are stored in a separate C++ module. Then the declaration of the class contains not only data members but also the prototype of member functions. The header file containing the description (.H) and the module storing the definition of member functions (.CPP) have the same name in general and refer to their corresponding class.

The description of the class Point in the Point.h header file:

#ifndef __PONT_H__

The name of member functions has to be qualified with the name of the class (::) in the file Point.cpp:

#include <iostream>

Of course, the keyword inline can be used explicitly for member functions; however, in that case, the definition of inline member functions has to be moved from the C++ module to the header file:

#ifndef __PONT_H__

void Move(int a, int b); code. However, the friend mechanism makes it possible for us to access the private and protected members of a class from a function outside the class.

A friend declaration can be placed anywhere within the description of that class. An external function, a member function of another class and even a whole other class (that is all of its member functions) can be a

"friend". Accordingly, a friend declaration consists of the prototype of the functions and the name of the class, the whole introduced by the keyword class.

It should be noted that a "friend" relationship is not mutual in the case of a friend class because it is only the member functions of the class figuring in the friend declaration that have full access to the members of the class containing that description.

In the following example, the external function Sum(), the public member function named Count() of BClass as well as all data functions of AClass have full access to all members of CClass:

class AClass;

friend int BClass::Count(int x);

friend class AClass; members be quickly accessed, a direct access is needed, and this can be realised by the "friend" mechanism.

#include <iostream>

void SetY(int a) { y = a; }

void Move(int a, int b) { x = a; y = b; } void Move(const Point& p) { x = p.x; y = p.y; }

void PrintOut() const { cout<<"("<<x<<","<<y<<")\n"; } };

double Distance(const Point & p1, const Point & p2) { return sqrt(pow(p1.x-p2.x,2.0)+pow(p1.y-p2.y,2.0));

2.2.4. What can we also add to classes?

Until now, we have only placed data and functions in classes. We have also added static constants and the type enum to classes.

In the following parts, we will see how to put constants and references in classes, and after that we will also summarise the rules of using nested classes.

2.2.4.1. Constant data members of objects

There are cases where a unique constant value is needed to be assigned to object instances, for example a name or an identifier. It is possible if the the data member is preceded by the keyword const and if it is added to the member initialisation list of constructors.

In the following example, objects of type User are created and the public name of users are used as constants:

class User {

void SetPassword(string newpsw) { password = newpsw;}

};

It should be noted that the traditional member by member copy does not work between objects of the same type if these contain constant members.

2.2.4.2. Reference type data members

Since a reference can only be created for an already existing variable, the same is true for references like for constants when objects are constructed. In the following example, the sensor object is linked with the controller object by a reference:

class Sensor { private:

int data;

public:

void ReceiveData() { cout<<sensor.Read(); } };

It is frequent to place the object instance of a class in another class as a data member. It is an important rule that when objects of a class like this are created, the inner objects also have to be initialised, which can be achieved by adding them to the member initialisation list of the appropriate constructor call.

Calling a constructor is not necessary if the class of the member object has a constructor without a parameter (default constructor), which is automatically called.

The previous controller-sensor example code can be modified in order that the sensor appear as an object in the controller object:

void ReceiveData() { cout<<sensor.Read(); } }; difference between these pointers and traditional ones is that the later detailed polymorphism also has its effect if virtual member functions are called with pointers.)

In order to define pointers correctly, we have to use the name of the class and the scope operator:

class Class; forward class declaration,

int Class::*p; p is a pointer to a data member of type int,

void (Class::*pfunct)(int); pfunct may point to a member function that is called with an argument of type int and that returns no value.

In the following example, we present how to use pointers to class members. We will access both class members by pointers. If such pointers are used, the members are referenced by the operators .* (dot asterisk) and ->*

(arrow asterisk) instead of traditional operators. In order that the address of data members and member functions could be obtained, the "address of" operator (&) has to be used.

#include <iostream> void (Class::* functptr)(int) = &Class::f;

By using typedef, expressions containing pointers are easier to be handled:

typedef int Class::*pointer_int; operations has meant calling member functions. Program codes become more legible if an operator of a similar content can be used instead of calling functions.

C++ offers the possibility to link a function created by a programmer to a traditional operator, extending the functioning of that operator. This function is called automatically if an operator is used in a specific context.

In document Mechatronic Systems Programming in C++ (Pldal 186-193)