Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Interfacing with C plus plus-programing communication with microcontrolers (K. Bentley, 2006)

.pdf
Скачиваний:
192
Добавлен:
12.08.2013
Размер:
3.18 Mб
Скачать

276 9 PROGRAM DEVELOPMENT TECHNIQUES

void SetY(int y); int GetX();

int GetY();

};

A suitable class definition for the Crane class would be:

class Crane

{

private:

Point A, B;

DCMotor LiftMotor, Xmotor, Ymotor;

public:

Crane();

void SetPointA(Point a); void SetPointB(Point b); void MoveToPoint();

void LiftLoad(); void LowerLoad();

};

All member functions of the Crane class that generate movement will need to use the Forward(), Reverse(), and Brake() functions of the DCMotor class.

As demonstrated here, structured programming starts with good pseudo-code written with the program steps outlined in a structured manner. Each of these steps forms a statement in the main() function. The member functions of the class definition are expanded to include the necessary details for the function to perform their planned tasks. The following section describes a good approach for coding the AbstractMotor class and its member functions.

Creating functions starting from class definitions.

A few basic principles should be kept in mind when coding object classes and their member functions. In general, the members of a class can be listed anywhere within the scope of the class definition (between the open and close braces) under different access attributes. However, keeping the member data together and separate from the member functions can facilitate coding.

The object class will often have a user-defined constructor that is used to initialise all its data members. Having placed data members together allows them to be copied as a block and placed into the body of the constructor for initialisation. They can also be copied and pasted elsewhere to code the definitions of other functions. The bodies of the member functions can be left empty and the source code then compiled to verify that it conforms to the syntax used by the C++ language.

Consider the AbstractMotor class definition from Listing 8-1 in Chapter 8 that has been reproduced in Listing 9-1.

9 PROGRAM DEVELOPMENT TECHNIQUES 277

Listing 9-1 AbstractMotor class definition.

class AbstractMotor

{

private:

int Speed;

public:

AbstractMotor();

void SetSpeed(int speed); int GetSpeed();

virtual void Off()=0; virtual void Forward()=0; virtual void Reverse()=0; virtual void Brake()=0;

};

In this class definition the data and functions are kept separate. It allows us to copy the set of non-pure virtual functions (that need to be defined) to a position just below the class definition as shown in Listing 9-2. Note that the pure virtual functions do not need to be copied since they will not have a function definition. It is good practice to keep the pure virtual functions together, preferably at the end of the member function declarations. This makes it easier to copy and paste the group of member function declarations we need to define.

Listing 9-2 Set of member functions copied to be defined.

class AbstractMotor

{

private:

int Speed;

public:

AbstractMotor();

void SetSpeed(int speed); int GetSpeed();

virtual void Off()=0; virtual void Forward()=0; virtual void Reverse()=0; virtual void Brake()=0;

};

AbstractMotor();

void SetSpeed(int speed); int GetSpeed();

278 9 PROGRAM DEVELOPMENT TECHNIQUES

This file cannot be compiled in its present form since it has incomplete and therefore incorrect syntax. It must be modified to give the functions the required basic syntax. To do this, each function name just copied must be qualified with the class name followed by the double colon operator (::). In the case of the AbstractMotor() function shown in Listing 9-2, we need to have:

AbstractMotor::AbstractMotor()

The semicolon at the end of each line must be removed and an open brace and a close brace added to provide the start and the end of each member function. These changes to Listing 9-2 are shown in Listing 9-3.

Listing 9-3 Skeletal member functions for the AbstractMotor class.

class AbstractMotor

{

private:

int Speed;

public:

AbstractMotor();

void SetSpeed(int speed); int GetSpeed();

virtual void Off()=0; virtual void Forward()=0; virtual void Reverse()=0; virtual void Brake()=0;

};

AbstractMotor::AbstractMotor()

{

}

void AbstractMotor::SetSpeed(int speed)

{

}

int AbstractMotor::GetSpeed()

{

}

This file can now be compiled. Although it does not have any statements within the bodies of the member functions, correct program syntax has been applied. Note that the contents of the file cannot be linked to form an executable file without having a main() function. However, compiling will generate object code. There

9 PROGRAM DEVELOPMENT TECHNIQUES 279

is an advantage to compiling the file at this early stage; we can verify the structure of the class has correct syntax.

Skeletal functions

Function definitions with empty bodies (as shown in Listing 9-3) can be referred to as skeletal functions. The bodies of these functions could be coded at this early stage of program development. However, they are intentionally left empty to simplify the task of establishing good program structure and correct syntax. It can even be advantageous to write an entire program using skeletal functions to verify its conceptual operation and structure.

Filling-in the constructors’ bodies

The next stage is to code the bodies of the member functions, starting say with the constructor definition. As mentioned earlier, the most common purpose of the constructor is to initialise the data members of the class. This can be done by copying and pasting the entire set of data members into the body of each constructor of the class. In the example discussed above, we only have one data member. Listing 9-4 shows the constructor that has this data member copied and pasted into its body. Note that this function is incomplete and does not carry out its intended task.

Listing 9-4 Copying the member data declarations into the body of the constructor.

AbstractMotor::AbstractMotor()

{

int Speed;

}

This single statement shown in Listing 9-4 needs to be corrected as shown in Listing 9-5. Once this is done the constructor can initialise this data member and operate as intended.

Listing 9-5 The constructor with the syntax error eliminated.

AbstractMotor::AbstractMotor()

{

Speed = 0; // copied and pasted data members are // initialized as required.

}

Good habits with parenthesis, square brackets and braces

Pairs of parentheses (), square brackets [], and braces {} are used extensively in C++ programming. We’ll use the general term brackets to describe all three types. When writing text we sometimes place an open parenthesis and forget to place the

280 9 PROGRAM DEVELOPMENT TECHNIQUES

close parenthesis. People use their intelligence to check and correct missing parentheses, however, a compiler lacks this ability. As a result, the programmer must correct for missing brackets. The misuse of brackets generally happens when the programmer loses track of a close bracket, and this generates errors when compiling the program. It can be difficult to determine the position in the program where the missing bracket should be placed once the program statements have been added. This situation is further complicated when nested brackets are used.

The best approach to avoid these problems is to place the matching open and close brackets simultaneously. The following techniques can be used to efficiently implement a class definition:

Step 1:

class AbstractMotor

{

};

Step 2:

class AbstractMotor

{

private:

int Speed;

public:

AbstractMotor();

void SetSpeed(int speed); int GetSpeed();

virtual void Off()=0; virtual void Forward()=0; virtual void Reverse()=0; virtual void Brake()=0;

}

The following steps show how the function declarations are developed:

Step 1:

void SetSpeed();

Step 2:

void SetSpeed(int speed)

{

}

Step 3:

void SetSpeed(int speed)

9 PROGRAM DEVELOPMENT TECHNIQUES 281

{

int Speed;

}

Step 4:

void SetSpeed(int speed)

{

Speed = 0;

}

Nested Levels

The following steps show how a nested if statement is developed:

Step 1:

if()

{

}

else

{

}

Step 2:

if()

{

if()

{

}

else

{

}

}

else

{

}

Indentation is then used to clearly show the levels of nesting. The skeletal if statement shown above would then become:

if()

{

if()

{

}

else

{

282 9 PROGRAM DEVELOPMENT TECHNIQUES

}

}

else

{

}

Applying the same habits will help you type error-free code when using logical operators as part of conditional expressions in if statements:

if()

{

if(() && ())

{

}

else

{

}

}

else

{

}

We can complete the conditional expression as shown in this example:

if(b > 0)

{

if((a != 0) && (b/a > n))

{

}

else

{

}

}

else

{

}

9.3 Modular Programs

Each program developed in previous chapters has all its program statements contained in one file. While this is satisfactory for smaller programs, it becomes less practical as programs grow in size and complexity. Larger programs have specific portions of their code separated into modules and stored as separate files. Because more than one file needs to be compiled and linked, the program becomes known as a multiple file program.

There are additional reasons why programs are developed in a multiple file format.

9 PROGRAM DEVELOPMENT TECHNIQUES 283

In the case of object-oriented programming, the files can be treated as modules where each module contains the code for one object class. As a result, when distributing software relating to a particular object, only the code for that object needs to be distributed. Software developers typically supply object code modules (unreadable to the users) accompanied by files that allow the object code to be used and linked with a program. This helps to prevent the developers’ object code being illegally used or misused.

When a particular object is used, only the file that corresponds to that one object needs to be included in the user code. This helps to minimise the size of a program. Had multiple objects been part of the file, the additional unused functions for those objects would also be compiled, slowing the compilation process.

9.3.1 Separating Software into Modules

AbstractMotor

ParallelPort

Class

Class

Motor

Class

DCMotor

StepperMotor

Class

Class

Figure 9-1 Class hierarchy for Motor Driver class (Chapter 8).

We will use the motor driver program developed in Chapter 8 (class hierarchy shown in Figure 9-1) to explain the process of generating a multiple file program. Each class is separated into two types of files. The class definition is placed into a header file, and the definitions of member functions for that class are placed into a function file (Figure 9-2). The remaining file for this example program (not shown) is generated from the main() function (note that this is not always the case).

The compiler needs to access the class definitions in the header file and function definitions in the function file. The function file (as source code; *.cpp) is compiled into an .obj file or .lib file by the developer. The programmer that

284 9 PROGRAM DEVELOPMENT TECHNIQUES

uses these files cannot read them, and so the developer’s software is protected from unauthorised copying and misuse.

Class

// class definition

class DCMotor : public Motor

{

public:

DCMotor(int baseaddress=0x378); virtual void Forward();

virtual void Reverse(); virtual void Brake();

};

// member f’n 1 definition DCMotor::DCMotor(...)

{

.

.

.

}

// member f’n 2 definition DCMotor::Forward()

{

.

.

.

}

// member f’n 3 definition DCMotor::Reverse()

{

.

.

.

}

// member f’n 4 definition DCMotor::Brake()

{

.

.

.

}

// member f’n 5 definition DCMotor::Off()

{

.

.

.

}

Class definition

// class definition

class DCMotor : public Motor

{

public:

DCMotor(int baseaddress=0x378); virtual void Forward();

virtual void Reverse(); virtual void Brake();

};

Header File (*.h)

Member function definitions

// member f’n 1 definition DCMotor::DCMotor(...)

{

.

.

.

}

// member f’n 2 definition DCMotor::Forward()

{

.

.

.

}

// member f’n 3 definition DCMotor::Reverse()

{

.

.

.

}

// member f’n 4 definition DCMotor::Brake()

{

.

.

.

}

// member f’n 5 definition DCMotor::Off()

{

.

.

.

}

Function File (*.cpp or *.obj or *.lib)

Note: a *.cpp file is shown here

Figure 9-2 Code separation into header and function files.

9 PROGRAM DEVELOPMENT TECHNIQUES 285

Object-oriented and non-object-oriented programs differ; in one case the functions are member functions, and in the other case all the functions are non-member functions. It is also possible for an object-oriented program to use a combination of the two cases. Its header file may contain a class definition and also some nonmember function declarations.

If non-member C/C++ functions are to be used in the program, it is general practice to put the declarations of such functions into a header file and then to include that header file at the top of the source file. The definitions of the non-member functions may be provided in a separate function file in much the same way as the member functions of a particular class. The function file in its .obj/.lib form will be needed at linking time.

9.3.2 Generating a Multiple File Program

Even inside a header file, the header file corresponding to each object used (in this header file) is included.

#include <stdlib.h>

 

 

 

#include “motor.h”

Header files

are

#include “dcmotor.h”

included for

 

#include “stepper.h”

each object

 

void main()

used.

 

 

 

 

{

 

 

 

.

.

.

}

User Program (main.cpp)

#include “motor.h”

class DCMotor : public Motor

{

public:

DCMotor(int baseaddress=0x378); virtual void Forward();

virtual void Reverse(); virtual void Brake(); virtual void Off();

};

DCMotor class header file (dcmotor.h)

Figure 9-3 Use of include directives.

As mentioned previously, whenever an object from a class is needed within a program, its class header file needs to be included in the program before the object can be used. The compiler will first interpret the class definition (from the first header file encountered) and then check that the class has been implemented correctly throughout the remainder of the program. The program will only compile correctly if the programmer has made function calls that are compatible with the function declarations in the header files. Figure 9-3 shows the use of the Motor, DCMotor, and StepperMotor objects in the User Program, evident by the inclusion of their class header files.